在客户端/服务器环境中报告进度
报告长时间运行的服务器操作的进度时,出现奇怪的问题。 该应用程序具有客户端/服务器体系结构,并用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
}
任何帮助将不胜感激。提前致谢。
谢谢大家的意见。
的原因问题是另一个过程,在不同的线程是通过自己的Dispatcher.Invoke访问服务器的方法,造成锁。这一流程初创企业非常罕见 - 因此它在一段时间后留下了锁定的印象。
总体建议我可以给是让Dispatcher.Invoke/BeginInvoke的方法尽可能的轻,没有任何重的计算内。事先做好你的服务器工作,并使用它们来更新UI。
我怀疑Backgroundworker不适合以这种方式使用远程编组。
离开的BackgroundWorker在客户端,不传,并设置一个事件接收器,它是一个MarshalByRefObject的它仍然在客户端上,被称为/从服务器发出信号。
接收器反过来可以调用Backgroundworker上的方法。
在这里使用的远程处理需要更多的细节(可能是代码)。 – Nayan
尝试使用Dispatcher类和DispatcherPriority.Background来设置progressBar值。 – vorrtex
不幸的是,我不能提供服务器端操作执行的详细代码,因为他们需要数千行。远程设置没有什么特别的,在任何情况下都能很好地工作。问题不会立即发生。进度报告,然后过了一段时间,重度行动来了 - 用户界面卡住了。如果有什么事情导致这种行为,我会很高兴检查。谢谢。 – Amid