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. apache URL重写 标志表 以及 错误解决方法

    Apache mod_rewrite规则重写的标志一览 1) R[=code](force redirect) 强制外部重定向 强制在替代字符串加上http://thishost[:thisport] ...

  2. 【链表】Odd Even Linked List

    题目: Given a singly linked list, group all odd nodes together followed by the even nodes. Please note ...

  3. CAJ Viewer安装流程以及CAJ或Pdf转换为Word格式

        不多说,直接上干货! pdf转word格式,最简单的就是,实用工具 Adobe Acrobat DC 首先声明的是,将CAJ或者Pdf转换成Word文档,包括里面的文字.图片以及格式,根本不需 ...

  4. PHP PSR 标准

    引用他人文章:http://www.cnblogs.com/52php/p/5852572.html PHP中PSR-[0-4]代码规范 PHP-FIG 在说啥是PSR-[0-4]规范的之前,我觉得我 ...

  5. mysql空间扩展 VS PostGIS

    http://www.cnblogs.com/LBSer/p/3629149.html 功能 Mysql spatial extension  PostGIS 空间索引 仅MyISAM支持R树索引,I ...

  6. Nodejs学习笔记(一)—简介及安装Node.js开发环境

    一.简介 Node.js是让Javascript脱离浏览器运行在服务器的一个平台,不是语言: Node.js采用的Javascript引擎是来自Google Chrome的V8:运行在浏览器外不用考虑 ...

  7. Oracle 12c 操作 CDB PDB

    CREATE TRIGGER open_all_pdbs AFTER STARTUP ON DATABASE BEGIN EXECUTE IMMEDIATE 'alter pluggable data ...

  8. 【LeetCode题解】206_反转链表(Reverse-Linked-List)

    目录 描述 解法一:迭代 思路 Java 实现 Python 实现 复杂度分析 解法二:递归 思路 Java 实现 Python 实现 复杂度分析 更多 LeetCode 题解笔记可以访问我的 git ...

  9. 17.async 函数

    async 函数 async 函数 含义 ES2017 标准引入了 async 函数,使得异步操作变得更加方便. async 函数是什么?一句话,它就是 Generator 函数的语法糖. 前文有一个 ...

  10. val();html();.text()区别

    对于innerHTML 属性,几乎所有的元素都有innerHTML属性,它是一个字符串,用来设置或获取位于对象起始和结束标签内的HTML.(获取HTML当前标签的起始和结束里面的内容) 对于inner ...