使用BackgroundWorker组件进行异步操作编程
时间:2010-11-08 来源:viper2010
publicvoidRunWorkerAsync(); publicvoidRunWorkerAsync(Object argument); |
publicvoid CancelAsync(); |
publicvoidReportProgress(int percentProgress); publicvoidReportProgress(int percentProgress,Object userState); |
publicboolCancellationPending { get; } publicboolWorkerReportsProgress { get; set; } publicboolWorkerSupportsCancellation { get; set; } |
publicbool IsBusy { get; } |
publiceventDoWorkEventHandler DoWork; publiceventProgressChangedEventHandler ProgressChanged; publiceventRunWorkerCompletedEventHandler RunWorkerCompleted; |

下面对主要实现代码进行说明,先看一下BackgroundWorker类的初始化,在初始化过程中注册了3个事件,允许异步辅助方法调用,以及异步操作进度通知和操作取消。
通过StartAsync按钮事件处理程序开始异步处理操作请求,事件处理程序如下,
startAsyncButton_Click处理程序首先对一些界面控件进行状态设置,然后调用BackgroundWorker实例的RunWorkerAsync方法开始执行异步操作,而此时就会触发DoWork事件。
在DoWork事件处理程序中,通过DoWorkEventArgs.Argument属性获取传入的参数传递给ComputeAdd辅助方法,并把处理结果保存到DoWorkEventArgs.Result属性中,最后在RunWorkerCompleted 事件处理程序的RunWorkerCompletedEventArgs.Result 属性中获取处理结果。如果在DoWork事件处理程序中出现异常,则 BackgroundWorker 将捕获该异常并将其传递到 RunWorkerCompleted 事件处理程序,在该事件处理程序中,异常信息作为 RunWorkerCompletedEventArgs 的 Error 属性公开。
在辅助方法中,代码定期访问BackgroundWorker实例的CancellationPending属性,如果调用了BackgroundWorker的CancelAsync 方法,那么CancellationPending属性值就会被设置为true,辅助方法就结束执行。另外,在辅助方法中实现了进度汇报功能,通过调用worker.ReportProgress方法触发ProgressChanged事件,接着通过ProgressChanged事件处理程序来更新进度显示。
最后,在RunWorkerCompleted事件处理程序中可以得到异步处理结果信息,分析异步操作是正常执行结束还是在处理中被取消或者是执行出现错误异常而终止。对于处理结果信息的访问有一个标准的顺序,先是判断异步处理是否异常结束,接着判断是否执行了取消操作,最后访问处理结果。
上面的例子是在单个窗口中完成所有功能,可以对其进行简单的修改实现在独立对话框中显示进度并提供取消操作的功能。
图2:进度显示对话框
新建一个窗体命名为ProcessForm用来显示异步操作进度,对ProcessForm类的默认构造函数进行修改,传入BackgroundWorker实例的引用,注册ProgressChanged事件实现窗体进度条的更新,注册RunWorkerCompleted事件通知ProcessForm窗体关闭。
对于进度窗口的显示方式可以是模式窗口或非模式窗口,两者的实现代码并没有太大区别,改进后的StartAsync按钮事件处理程序如下。
实现原理
在分析BackgroundWorker实现原理之前,需要了解一下在.NET Framework 2.0版本中新增加的两个类。AsyncOperationManager 类和AsyncOperation 类都位于System.ComponentModel 命名空间中,AsyncOperation类提供了对异步操作的生存期进行跟踪的功能,包括操作进度通知和操作完成通知,并确保在正确的线程或上下文中调用客户端的事件处理程序。
通过在异步辅助代码中调用Post方法把进度和中间结果报告给用户,如果是取消异步任务或提示异步任务已完成,则通过调用PostOperationCompleted方法结束异步操作的跟踪生命期。在PostOperationCompleted方法调用后,AsyncOperation对象变得不再可用,再次访问将引发异常。在两个方法中都包含SendOrPostCallback委托参数,签名如下,
SendOrPostCallback 委托用来表示在消息即将被调度到同步上下文时要执行的回调方法。
AsyncOperation类看上去很强大,不过有开发人员反映该类的.NET 2.0版本存在Bug,在3.0及后面的版本微软是否进行过更新还需进一步考证。笔者在控制台应用程序中进行测试,asyncOperation的Post方法递交的SendOrPostCallback委托不一定是在控制台主线程执行,通过访问System.Threading.Thread.CurrentThread.ManagedThreadId可以确认这一点,奇怪的是控制台程序未发现运行异常,这个可能是控制台程序执行方式不同于窗体程序的原因。
AsyncOperationManager 类为AsyncOperation对象的创建提供了便捷方式,通过CreateOperation方法可以创建多个AsyncOperation实例,实现对多个异步操作进行跟踪。
BackgroundWorker组件通过DoWork事件实现了在单独的线程上执行操作,其内部通过异步委托来完成,在BackgroundWorker类内部声明了WorkerThreadStartDelegate委托,并定义了threadStart成员变量,同时在构造函数中初始化threadStart。
BackgroundWorker通过调用RunWorkerAsync方法开始执行异步操作请求,并在方法体中调用threadStart.BeginInvoke方法实现异步调用。
在threadStart委托中指定的WorkerThreadStart方法将触发DoWork事件,使用者通过注册DoWork事件执行异步代码的操作,从下面的代码可以看出在DoWork事件处理程序中不能访问UI元素的原因。
在上述代码中,this.OnDoWork(e)方法产生DoWork事件,DoWork事件处理程序执行完成后会判断在事件处理程序中是否对DoWorkEventArgs.Cancel属性进行了设置,如果使用者调用了CancelAsync 方法那么DoWorkEventArgs.Cancel会被设置为true,事件处理程序正常执行完成时可以从DoWorkEventArgs.Result得到执行结果,如果出现处理异常将扑获异常,所有需要的信息将包含在RunWorkerCompletedEventArgs实例中,最后执行asyncOperation.PostOperationCompleted方法产生RunWorkerCompleted 事件,因此在RunWorkerCompleted事件处理程序中可以获得取消操作、处理异常或处理结果的信息。
类似于RunWorkerCompleted事件的发生机制,对于异步操作进度通知事件发生通过ReportProgress方法实现。
调用者在DoWork事件处理程序中通过调用ReportProgress方法进行进度汇报,其内部通过asyncOperation.Post方法产生ProgressChanged 事件,如果asyncOperation为null,那么就调用progressReporter方法产生事件,但是调用progressReporter方法产生事件明显存在问题,因为这样产生的事件所在线程同DoWork事件为同一线程,ProgressChanged事件处理程序也会执行在DoWork线程同一上下文中,因此在ProgressChanged事件处理程序中访问ProgressBar控件将出现“线程间操作无效: 从不是创建控件“progressBar1”的线程访问它。”的异常。笔者认为这样的处理是组件的一个Bug,如果asyncOperation为null,更好的处理方式是抛出异常或不做通知处理。值得一提的是,在控制台应用程序中测试调用progressReporter方法不会出现“线程间操作无效”的异常。
结合构造函数,下面的代码有助于进一步理解ProgressChanged事件和RunWorkerCompleted事件产生机制。
最后,看一下RunWorkerAsync方法和CancelAsync方法的实现。
结束语
BackgroundWorker组件简化了基于事件的异步操作编程,根据其实现原理可进一步编写支持多任务的异步操作组件来更好的满足异步操作密集的应用开发需求。
private System.ComponentModel.BackgroundWorker backgroundWorker1;
private void InitializeBackgoundWorker()
{
this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
this.backgroundWorker1.WorkerReportsProgress = true;
this.backgroundWorker1.WorkerSupportsCancellation = true;
this.backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
this.backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
this.backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
}
|
private void startAsyncButton_Click(object sender, EventArgs e) { resultLabel.Text = String.Empty; this.numericUpDown1.Enabled = false; this.startAsyncButton.Enabled = false; this.cancelAsyncButton.Enabled = true; //获取计算数值. int numberToCompute = (int)numericUpDown1.Value; //启动异步操作. backgroundWorker1.RunWorkerAsync(numberToCompute); } |
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
e.Result = ComputeAdd((int)e.Argument, worker, e);
}
|
private long ComputeAdd(int n, BackgroundWorker worker, DoWorkEventArgs e) { long result = 0; for (int i = 1; i <= n; i++) { if (worker.CancellationPending) { e.Cancel = true; break; } else { result += i; Thread.Sleep(500); int percentComplete = (int)((float)i / (float)n * 100); worker.ReportProgress(percentComplete); } } return result; } |
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { this.progressBar1.Value = e.ProgressPercentage; } |
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show(e.Error.Message);
}
else if (e.Cancelled)
{
resultLabel.Text = "Canceled";
}
else
{
resultLabel.Text = e.Result.ToString();
}
this.numericUpDown1.Enabled = true;
startAsyncButton.Enabled = true;
cancelAsyncButton.Enabled = false;
}
|

public ProcessForm(BackgroundWorker backgroundWorker1) { InitializeComponent(); this.backgroundWorker1 = backgroundWorker1; this.backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged); this.backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted); } void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { this.Close(); } void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { this.progressBar1.Value = e.ProgressPercentage; } private void cancelButton1_Click(object sender, EventArgs e) { this.backgroundWorker1.CancelAsync(); this.cancelButton1.Enabled = false; this.Close(); } |
private void startAsyncButton_Click(object sender, EventArgs e) { // ... backgroundWorker1.RunWorkerAsync(numberToCompute); ProcessForm form = new ProcessForm(this.backgroundWorker1); form.ShowDialog(this);//模式 //form.Show(this);//非模式 } |
publicvoidPost(SendOrPostCallback d,Object arg); publicvoidPostOperationCompleted(SendOrPostCallback d,Object arg); |
publicdelegatevoidSendOrPostCallback(Object state); |
private delegate void WorkerThreadStartDelegate(object argument);
private readonly WorkerThreadStartDelegate threadStart;
public BackgroundWorker()
{
this.threadStart = new WorkerThreadStartDelegate(this.WorkerThreadStart);
//…
}
|
public void RunWorkerAsync(object argument) { if (this.isRunning) { throw new InvalidOperationException(SR.GetString("BackgroundWorker_WorkerAlreadyRunning")); } this.isRunning = true; this.cancellationPending = false; this.asyncOperation = AsyncOperationManager.CreateOperation(null); this.threadStart.BeginInvoke(argument, null, null); } |
private void WorkerThreadStart(object argument) { object result = null; Exception error = null; bool cancelled = false; try { DoWorkEventArgs e = new DoWorkEventArgs(argument); this.OnDoWork(e); if (e.Cancel) { cancelled = true; } else { result = e.Result; } } catch (Exception exception2) { error = exception2; } RunWorkerCompletedEventArgs arg = new RunWorkerCompletedEventArgs(result, error, cancelled); this.asyncOperation.PostOperationCompleted(this.operationCompleted, arg); } |
public void ReportProgress(int percentProgress, object userState) { if (!this.WorkerReportsProgress) { throw new InvalidOperationException(SR.GetString("BackgroundWorker_WorkerDoesntReportProgress")); } ProgressChangedEventArgs arg = new ProgressChangedEventArgs(percentProgress, userState); if (this.asyncOperation != null) { this.asyncOperation.Post(this.progressReporter, arg); } else { this.progressReporter(arg); } } |
public BackgroundWorker()
{
this.threadStart = new WorkerThreadStartDelegate(this.WorkerThreadStart);
this.operationCompleted = new SendOrPostCallback(this.AsyncOperationCompleted);
this.progressReporter = new SendOrPostCallback(this.ProgressReporter);
}
private void ProgressReporter(object arg)
{
this.OnProgressChanged((ProgressChangedEventArgs)arg);
}
private void AsyncOperationCompleted(object arg)
{
this.isRunning = false;
this.cancellationPending = false;
this.OnRunWorkerCompleted((RunWorkerCompletedEventArgs)arg);
}
|
public void RunWorkerAsync(object argument) { if (this.isRunning) { throw new InvalidOperationException(SR.GetString("BackgroundWorker_WorkerAlreadyRunning")); } this.isRunning = true; this.cancellationPending = false; this.asyncOperation = AsyncOperationManager.CreateOperation(null); this.threadStart.BeginInvoke(argument, null, null); } public void CancelAsync() { if (!this.WorkerSupportsCancellation) { throw new InvalidOperationException(SR.GetString("BackgroundWorker_WorkerDoesntSupportCancellation")); } this.cancellationPending = true; } |
相关阅读 更多 +