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

BackgroundWorker m_worker = new BackgroundWorker();
// 设置支持进度报告、异步取消功能,默认都为false
m_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 = ; i < ; 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

BackgroundWorker原理剖析的更多相关文章

  1. ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件)

    ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件) Startup Class 1.Startup Constructor(构造函数) 2.Configure ...

  2. ASP.NET Core 运行原理剖析1:初始化WebApp模版并运行

    ASP.NET Core 运行原理剖析1:初始化WebApp模版并运行 核心框架 ASP.NET Core APP 创建与运行 总结 之前两篇文章简析.NET Core 以及与 .NET Framew ...

  3. 【Xamarin挖墙脚系列:Xamarin.IOS机制原理剖析】

    原文:[Xamarin挖墙脚系列:Xamarin.IOS机制原理剖析] [注意:]团队里总是有人反映卸载Xamarin,清理不完全.之前写过如何完全卸载清理剩余的文件.今天写了Windows下的批命令 ...

  4. 【Xamarin 跨平台机制原理剖析】

    原文:[Xamarin 跨平台机制原理剖析] [看了请推荐,推荐满100后,将发补丁地址] Xamarin项目从喊口号到现在,好几个年头了,在内地没有火起来,原因无非有三,1.授权费贵 2.贵 3.原 ...

  5. iPhone/Mac Objective-C内存管理教程和原理剖析

    http://www.cocoachina.com/bbs/read.php?tid-15963.html 版权声明 此文版权归作者Vince Yuan (vince.yuan#gmail.com)所 ...

  6. 【Xamain 跨平台机制原理剖析】

    原文:[Xamain 跨平台机制原理剖析] [看了请推荐,推荐满100后,将发补丁地址] Xamarin项目从喊口号到现在,好几个年头了,在内地没有火起来,原因无非有三,1.授权费贵 2.贵 3.原生 ...

  7. Python字符串原理剖析------万恶的+号

    字符串原理剖析pyc文件,执行python代码时,如果导入了其他的.py文件,那么执行过程中会自动生成一个与其同名的.pyc文件,该文件就是python解释器变异之后产生的字节码 PS:代码经过编译可 ...

  8. MapReduce/Hbase进阶提升(原理剖析、实战演练)

    什么是MapReduce? MapReduce是一种编程模型,用于大规模数据集(大于1TB)的并行运算.概念"Map(映射)"和"Reduce(归约)",和他们 ...

  9. ASP.NET Core 运行原理剖析

    1. ASP.NET Core 运行原理剖析 1.1. 概述 1.2. 文件配置 1.2.1. Starup文件配置 Configure ConfigureServices 1.2.2. appset ...

随机推荐

  1. Android Studio启动速度慢的问题。

    Android Studio每次启动都要去fetching sdk,由于Android sdk 官网在大陆连不上,所以每次启动时界面都会停在那里很久. 要提高启动速度,就要避免每次启动Android ...

  2. 解决chrome,下载在文件夹中显示,调用错误的关联程序

    https://blog.csdn.net/qq_32337527/article/details/81778732?utm_source=blogxgwz0

  3. CentOS6的python2.6升级到python2.7以上版本(可能更详细)

    前言:一些第三方框架为了降低复杂性,新的版本已经开始不支持旧版本的python,比如Django这个web框架1.8版本及以上仅仅只支持python2.7及以上版本(记忆中是这个1.8版本) pip安 ...

  4. freeSWITCH之多平台测试通信

    开始测试使用 强烈建议在统一的局域网下进行配置,通信 本机IP:192.168.1.155 架构 freeSWITCH搭建在以Windows平台作为通信服务器.fs_cli为服务器上测试客户端. X- ...

  5. CentOS6.5安装php7+nginx+mysql实现安装WordPress

    安装php7+nginx参考该博客http://blog.csdn.net/whatday/article/details/50645117 安装php7参考http://blog.csdn.net/ ...

  6. python 笔记-转

    python笔记   Python 学习笔记 - 14.技巧(Tips)   Python 学习笔记 - 13.异常(Exception)   Python 学习笔记 - 12.流程控制(Contro ...

  7. java.lang 类String

    方法摘要1  charcharAt(int index) 返回指定索引处的 char 值.               index - char 值的索引.2 string       concat( ...

  8. 一张图解决Struts2添加源码

    主要是选择的路径:F:/struts2/struts-2.3.31/src/core/src/main/java

  9. Hive集成HBase实践

    #step1: create hive table 't_test' hive -e "create table test.t_user(id int,name string,age int ...

  10. Linux系统资源查看 之 资源信息

    1. 系统 版本信息 内核版本 使用 uname 命令: -a : 查看所有系统信息 -r : 查看内核版本信息 -s : 查看内核名称 代码如下: [niesh@niesh ~]$ uname -a ...