2011-11-18 42 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