BackgroundWorker原理剖析

王朝学院·作者佚名  2016-05-16
窄屏简体版  字體: 小  |  中  |  大  |  超大  

BackgroundWorker原理剖析BackgroundWorker类位于System.ComponentModel命名空间下,主要用来异步执行一个长时间的操作,然后,在完成事件中安全更新UI的控件属性。UI中的控件是不允许非创建该控件的线程修改的。典型用法如下:

BackgroundWorker m_worker = new BackgroundWorker();// 设置支持进度报告、异步取消功能,默认都为falsem_worker.WorkerReportsPRogress = true;m_worker.WorkerSupportsCancellation = true;// 绑定事件m_worker.DoWork += m_worker_DoWork;m_worker.ProgressChanged += m_worker_ProgressChanged;m_worker.RunWorkerCompleted += m_worker_RunWorkerCompleted;void m_worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Cancelled == true) { // 处理取消 return; } else if (e.Error != null) { // 处理异常 return; } // 在UI中显示结果 // txtBox.Text = e.Result.ToString();}void m_worker_ProgressChanged(object sender, ProgressChangedEventArgs e) { //progressBar.Value = e.ProgressPercentage;}void m_worker_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker sendWorker = sender as BackgroundWorker; for (int i = 0; i < 100; i++) { // 做异步工作。。。。 // 报告进度 sendWorker.ReportProgress(i); // 请求取消工作内容 if (sendWorker.CancellationPending == true) { e.Cancel = true; return; } } // 可选,设置异步工作结果 e.Result = GetResultData();}

它的实现原理最重要的只有两点:

一点是用异步委托间接使用线程池执行长时间的操作;

另外一点是通过AsyncOperationManager和AsyncOperation对调用RunWorkerAsync的线程SynchronizationContext进行抽象;

BackgroundWorker的源码参见http://www.projky.com/dotnet/4.5.1/System/ComponentModel/BackgroundWorker.cs.html

首先从它的构造函数开始:

private delegate void WorkerThreadStartDelegate(object argument);private AsyncOperation asyncOperation = null;private readonly WorkerThreadStartDelegate threadStart;private readonly SendOrPostCallback operationCompleted;private readonly SendOrPostCallback progressReporter;public BackgroundWorker(){ threadStart = new WorkerThreadStartDelegate(WorkerThreadStart); operationCompleted = new SendOrPostCallback(AsyncOperationCompleted); progressReporter = new SendOrPostCallback(ProgressReporter);}

定义了一个私有的委托类型WorkerThreadStartDelegate,以便于在该委托类型对象上直接调用BaginInvoke到线程池执行委托。SendOrPostCallback 是方便在UI线程(本质是调用RunWorkAsync时捕获的当前线程同步上下文对象,为了容易理解,就叫它UI线程)上执行回调而创建的。而asyncOperation则通过Post方法在UI线程上异步来执行SendOrPostCallback委托。

在对DoWork添加事件后,需要调用RunWorkerAsync,有两个重载,但我们只关注最后一个带参数的:

public void RunWorkerAsync(object argument){ if (isRunning) { throw new InvalidOperationException(SR.GetString(SR.BackgroundWorker_WorkerAlreadyRunning)); } isRunning = true; cancellationPending = false; asyncOperation = AsyncOperationManager.CreateOperation(null); threadStart.BeginInvoke(argument, null, null);}

其实,asyncOperation = AsyncOperationManager.CreateOperation(null);这一行代码,等同于下面的代码:

if (SynchronizationContext.Current == null) { SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());}SynchronizationContext currentContext = SynchronizationContext.Current;asyncOperation = AsyncOperation.CreateOperation(null, currentContext)

简单来说,就是获得当前的SynchronizationContext的对象,如果不存在,则创建一个默认的(基于线程池实现的)。并让asyncOperation拥有SynchronizationContext的引用。

在.NET中,有很多种SynchronizationContext的子类,比如Winform里面的WindowsFormsSynchronizationContext类,WPF里面的DispatcherSynchronizationContext类,asp.net里面的AspNetSynchronizationContext类。重点是,当在Winform的UI线程中访问SynchronizationContext.Current属性,获得的就是WindowsFormsSynchronizationContext的对象。

那么,最终,AsyncOperation的Post方法,就是直接调用SynchronizationContext的Post方法,来实现在UI中回调的目的。

public void Post(SendOrPostCallback d, object arg){ VerifyNotCompleted(); VerifyDelegateNotNull(d); syncContext.Post(d, arg);}

还有一点,threadStart.BeginInvoke会用线程池中的线程执行类似如下的代码:

object workerResult = null;Exception error = null;bool cancelled = false;try{ DoWorkEventArgs doWorkArgs = new DoWorkEventArgs(argument); DoWorkEventHandler handler = (DoWorkEventHandler)(Events[doWorkKey]); if (handler != null) { handler(this, doWorkArgs); } if (doWorkArgs.Cancel) { cancelled = true; } else { workerResult = doWorkArgs.Result; }}catch (Exception exception){ error = exception;}RunWorkerCompletedEventArgs e = new RunWorkerCompletedEventArgs(workerResult, error, cancelled); asyncOperation.PostOperationCompleted(operationCompleted, e);

其中,对DoWork事件的声明如下:

private static readonly object doWorkKey = new object();public event DoWorkEventHandler DoWork{ add{ this.Events.AddHandler(doWorkKey, value); } remove{ this.Events.RemoveHandler(doWorkKey, value); }}

Events是从Component下派生来的protected EventHandlerList对象。

从BackgroundWorker的实现可以看出,它的实现是普遍性的,并不一定要用在Winform或者WPF中。

本文引用的源码参考列表可以从扣丁格鲁上查看。

http://www.projky.com/dotnet/4.5.1/System/ComponentModel/BackgroundWorker.cs.html

http://www.projky.com/dotnet/4.5.1/System/ComponentModel/AsyncOperation.cs.html

http://www.projky.com/dotnet/4.5.1/System/ComponentModel/AsyncOperationManager.cs.html

http://www.projky.com/dotnet/4.5.1/System/Threading/SynchronizationContext.cs.html

http://www.projky.com/dotnet/4.5.1/System/Windows/Forms/WindowsFormsSynchronizationContext.cs.html

http://www.projky.com/dotnet/4.5.1/System/Windows/Threading/DispatcherSynchronizationContext.cs.html

http://www.projky.com/dotnet/4.5.1/System/Web/AspNetSynchronizationContext.cs.html

关于基于事件的异步编程设计模式EAP更多参考请见http://msdn.microsoft.com/zh-cn/library/hkasytyf(v=vs.110).aspx

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航