2011-11-18 72 views
2

報告長時間運行的服務器操作的進度時,出現奇怪的問題。 該應用程序具有客戶端/服務器體系結構,並用C#編寫。客戶使用WPF。在客戶端/服務器環境中報告進度

在客戶端,我創建了進度窗口,並在後臺工作人員開始長時間運行的操作。此操作是通過遠程調用的服務器方法。由於參數服務器方法接受用於報告進度的特殊ProgressContext對象(請參閱下面的代碼)。

一旦服務器開始執行利用CPU /內存有些沉重的操作 - 進度窗口變得凍結。它不響應任何交互並且不更新進度。過了一段時間,繁重的操作完成後 - 進度窗口恢復活力,沒有發生任何事情。

它看起來當我通過後臺工作的實例的服務器和服務器線程就像是重負荷的 - 它的一些如何鎖定窗口BackgroundWorker的是有關。如果我沒有使用遠程調用使用相同的進度窗口 - 問題消失。

報告進度我使用進度窗口,BackgroundWorker的是整個網絡的許多樣品英寸 這裏是進度窗口C#代碼:

public partial class ProgressWindow : Window 
{ 
    #region Fields 

    public static readonly DependencyProperty AutoIncrementProperty = 
     DependencyProperty.Register(
      "AutoIncrement", 
      typeof(bool), 
      typeof(ProgressBar), 
      new UIPropertyMetadata(null)); 

    private readonly BackgroundWorker m_worker; 
    private CultureInfo m_culture; 
    private bool m_isCancelled; 
    private Exception m_error = null; 

    private Action<IProgressContext> m_workerCallback; 

    #endregion 

    #region Constructors 

    /// <summary> 
    /// Inits the dialog without displaying it. 
    /// </summary> 
    public ProgressWindow() 
    { 
     InitializeComponent(); 

     //init background worker 
     m_worker = new BackgroundWorker(); 
     m_worker.WorkerReportsProgress = true; 
     m_worker.WorkerSupportsCancellation = true; 

     m_worker.DoWork += Worker_DoWork; 
     m_worker.ProgressChanged += Worker_ProgressChanged; 
     m_worker.RunWorkerCompleted += Worker_RunWorkerCompleted; 

     AutoIncrement = true; 
     CancellingEnabled = false; 
    } 

    #endregion 

    #region Public Properties 

    public bool CancellingEnabled 
    { 
     get 
     { 
      return btnCancel.IsVisible; 
     } 
     set 
     { 
      btnCancel.Visibility = value ? Visibility.Visible : Visibility.Collapsed; 
     } 
    } 

    public bool Cancelled 
    { 
     get 
     { 
      return m_isCancelled; 
     } 
    } 

    public bool AutoIncrement 
    { 
     get 
     { 
      return (bool)this.GetValue(AutoIncrementProperty); 
     } 
     set 
     { 
      this.SetValue(AutoIncrementProperty, value); 
     } 
    } 

    public Exception Error 
    { 
     get 
     { 
      return m_error; 
     } 
    } 

    #endregion 

    #region Public Methods 

    public void Run(Action<IProgressContext> action) 
    { 
     if (AutoIncrement) 
     { 
      progressBar.IsIndeterminate = true; 
     } 

     //store the UI culture 
     m_culture = CultureInfo.CurrentUICulture; 

     //store reference to callback handler and launch worker thread 
     m_workerCallback = action; 
     m_worker.RunWorkerAsync(); 

     //display modal dialog (blocks caller) 
     ShowDialog(); 
    } 

    #endregion 

    #region Private Methods 

    #region Event Handlers 

    private void Worker_DoWork(object sender, DoWorkEventArgs e) 
    { 
     try 
     { 
      //make sure the UI culture is properly set on the worker thread 
      Thread.CurrentThread.CurrentUICulture = m_culture; 

      ProgressContext context = new ProgressContext((BackgroundWorker)sender); 

      //invoke the callback method with the designated argument 
      m_workerCallback(context); 
     } 
     catch (Exception) 
     { 
      //disable cancelling and rethrow the exception 
      Dispatcher.BeginInvoke(DispatcherPriority.Normal, 
            (SendOrPostCallback)delegate { btnCancel.SetValue(Button.IsEnabledProperty, false); }, 
            null); 
      throw; 
     } 
    } 

    private void btnCancel_Click(object sender, RoutedEventArgs e) 
    { 
     btnCancel.IsEnabled = false; 
     m_worker.CancelAsync(); 
     m_isCancelled = true; 
    } 

    private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e) 
    { 
     if (e.ProgressPercentage != int.MinValue) 
     { 
      progressBar.Value = e.ProgressPercentage; 
     } 

     if (e.UserState != null) 
     { 
      lblStatus.Text = (string)e.UserState; 
     } 
    } 

    private void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     if (e.Error != null) 
     { 
      m_error = e.Error; 
     } 

     //update UI in case closing the dialog takes a moment 
     btnCancel.IsEnabled = false; 

     Close(); 
    } 

    #endregion 

    #endregion 
} 

public class ProgressContext : MarshalByRefObject, IProgressContext 
{ 
    #region Fields 

    private BackgroundWorker m_worker; 

    #endregion 

    #region Constructors 

    public ProgressContext(BackgroundWorker worker) 
    { 
     m_worker = worker; 
    } 

    #endregion 

    #region Public Properties 

    public void ReportProgress(string message) 
    { 
     m_worker.ReportProgress(int.MinValue, message); 
    } 

    public void ReportProgress(int progress, string message) 
    { 
     m_worker.ReportProgress(progress, message); 
    } 

    public void ReportProgress(int progress) 
    { 
     m_worker.ReportProgress(progress); 
    } 

    public bool IsCancelled 
    { 
     get 
     { 
      return m_worker.CancellationPending; 
     } 
    } 

    #endregion 
} 

任何幫助將不勝感激。提前致謝。

+0

在這裏使用的遠程處理需要更多的細節(可能是代碼)。 – Nayan

+0

嘗試使用Dispatcher類和DispatcherPriority.Background來設置progressBar值。 – vorrtex

+0

不幸的是,我不能提供服務器端操作執行的詳細代碼,因爲他們需要數千行。遠程設置沒有什麼特別的,在任何情況下都能很好地工作。問題不會立即發生。進度報告,然後過了一段時間,重度行動來了 - 用戶界面卡住了。如果有什麼事情導致這種行爲,我會很高興檢查。謝謝。 – Amid

回答

0

謝謝大家的意見。

的原因問題是另一個過程,在不同的線程是通過自己的Dispatcher.Invoke訪問服務器的方法,造成鎖。這一流程初創企業非常罕見 - 因此它在一段時間後留下了鎖定的印象。

總體建議我可以給是讓Dispatcher.Invoke/BeginInvoke的方法儘可能的輕,沒有任何重的計算內。事先做好你的服務器工作,並使用它們來更新UI。

0

我懷疑Backgroundworker不適合以這種方式使用遠程編組。

離開的BackgroundWorker在客戶端,不傳,並設置一個事件接收器,它是一個MarshalByRefObject的它仍然在客戶端上,被稱爲/從服務器發出信號。

接收器反過來可以調用Backgroundworker上的方法。

+0

其實如果我找到了你,這正是我現在所擁有的 - ProgressContext類是MarshalByRef。它是通過遠程邊界的那個。當從服務器端調用它的方法時 - 在客戶端它調用它的m_worker相應的方法(請參閱原始消息中發佈的代碼)。或者你換個不同的東西? – Amid

+0

啊,是的,現在我明白你的代碼了。您是否將回調標記爲OneWay? –

+0

遠程處理部分未寫入WCF中。這是遠古的遠程處理。 – Amid