C# BackgroundWorker详解,图例,原理分析

先声明,大部分资料均参考网上,进行了整理。

1.

在 VS 中添加了 BackgroundWorker 组件, 该组件在多线程编程方面使用起来非常 方便,然而在开始时由于没有搞清楚它的使用机制,

走了不少的弯路,现在把我 在使用它的过程中的经验与诸位分享一下。

BackgroundWorker 类中主要用到的有这列属性、方法和事件:

重要属性:

1、CancellationPending 获取一个值,指示应用程序是否已 请求取消后台操作。

通过在 DoWork 事件中判断 CancellationPending 属性可以 认定是否需要取消后台操作(也就是结束线程);

2、IsBusy 获取一个值,指示 BackgroundWorker 是否正在运行异步操作。

程序中使用 IsBusy 属性用来确定后 台操作是否正在使用中;

3、WorkerReportsProgress 获取或设置一个值,该值指示 BackgroundWorker 能否报告进度更新

4、WorkerSupportsCancellation 获取或设置一个值,该值指示 BackgroundWorker 是否支持异步取消。

设置 WorkerSupportsCancellation 为 true 使得程序可以调用 CancelAsync 方法提交终止挂起的后台 操作的请求;

重要方法:

1、CancelAsync 请求取消挂起的后台操作

2、RunWorkerAsync 开始执行后台操作

3、ReportProgress 引发 ProgressChanged 事件

重要事件:

1、DoWork 调用 RunWorkerAsync 时发生

2、ProgressChanged 调用 ReportProgress 时发生

3、RunWorkerCompleted 当后台操作已完成、被取消或引发异常时发生 另外还有三个重要的参数是

RunWorkerCompletedEventArgs 以及 DoWorkEventArgs、ProgressChangedEventArgs。

BackgroundWorker 的各属性、方法、事件的调用机制和顺序:

C# BackgroundWorker详解,图例,原理分析

从上图可见在整个生活周期内发生了 3 次重要的参数传递过程:

参数传递 1:此次的参数传递是将 RunWorkerAsync(Object)中的 Object 传递到DoWork 事件的

DoWorkEventArgs.Argument,由于在这里只有一个参数可以 传递,所以在实际应用往封装一个类,

将整个实例化的类作为 RunWorkerAsync 的 Object 传递到 DoWorkEventArgs.Argument;

参数传递 2:此次是将程序运行进度传递给 ProgressChanged 事件,

实际使用中往往使用给方法和事件更新进度条或者日志信息;

参数传递 3:在 DoWork 事件结束之前,将后台线程产生的结果数据赋给

DoWorkEventArgs.Result 一边在 RunWorkerCompleted 事件中

调用 RunWorkerCompletedEventArgs.Result 属性取得后台线程产生的结果。

另外从上图可以看到 DoWork 事件是在后台线程中运行的,

所以在该事件中 不能够操作用户界面的内容,如果需要更新用户界面,

可以使用 ProgressChanged 事件及 RunWorkCompleted 事件来实现。

2. 凡是 WinForm 的应用程序,如果他执行了一个的非常冗长的处理操作(比如文件 查询),

它在执行时会锁定用户界面,虽然主活动窗口 一直在运行,但用户无 法与程序交互,

无法移动窗体或改变窗体大小,所以用户感觉很不爽。如何做才 能使得这个程序有响应。

答案就是在后台线程中执行这个操作。

在这里已经有了多种方法来做这个事情:

(一)  委托异步调用 将具体耗时的操作作为一个委托,并用 BeginInvoke 来异步执行这个委托

(Invoke 是同步调用),并且可以为这个操作传入参数并且通过 EndInvoke 方 法获得返回返回值。

(二)  使用 ThreadPool 新建.net FrameWork 中自带的 WaitCallback 委托,然后放到线程池

中运行 ThreadPool.QueueUserWorkItem( callback ); 根据 WaitCallback 委托的定义,

可以传入一个 object 类型的参数。 但是不能精确的控制线程池中的线程。

(三)  使用 Thread 和 ThreadPool 相比,使用 Thread 的开销会比较大。但是它有它的优势,

使用 Thread 类可以显式管理线程。只要有可能,就应该使用 ThreadPool 类来 创建线程。

然而,在一些情况下,您还是需要创建并管理您自己的线程,而不是 使用 ThreadPool 类。

.net 2.0 中,提供了一个新的委托 ParameterizedThreadStart 支持启动一个线程并传入参数,

这是对原来的 ThreadStart 委托的改进。

说了这么多还没有说到今天的主角 BackgroundWorker,他也是一个在 2.0 中 新增的类,可以用于启动后台

线程,并在后台计算结束后调用主线程 的方法.可 以看出同样的功能使用委托的异步调用也可以实现,只是

使用 BackgroundWorker 的话会更加的简便快捷,可以节省开发时间,

并把你从 创建 自己的委托以及对它们的调用中解救出来。真是这样的吗看看下面这个例子。

其 实我也是从 101Samples 中看到的例子。

先看看 BackgroundWorker 中的主要概念。

第一:主要的事件及参数。

DoWork——当执行 BackgroundWorker.RunWorkerAsync 方法时会触 发该事件,

并且传递 DoWorkEventArgs 参数;

ProgressChanged——操作处理中获得的处理状态变化,

通过 BackgroundWorker.ReportProgress(int)方法 触发该事件,

并且传递 ProgressChangedEventArgs,其中包含了处理的百分比;

RunWorkerCompleted ——异步操作完成后会触发该事件,当然如果需要在操作

过程中结束可以执行 BackgroundWorker.CancelAsync 方法要求异步调用中 止,

并且在异步委托操作中检测 BackgroundWorker.CancellationPending 属性如果 为 true 的话,

跳出异步调用,同时将 DoWorkEventArgs.Cancel 属性设为 true,这样当退出异步调用的时候,

可以让处理 RunWorkerCompleted 事件的函数 知道 是正常退出还是中途退出。

第二:主要的方法。

BackgroundWorker.RunWorkerAsync—— “起动”异步调用的方法有两次重载

RunWorkerAsync(),RunWorkerAsync(object argument),第二个重载提供了一个 参数,

可以供异步调用使用。(如果有多个参数要传递怎么办,使用一个类来传 递他们吧)。

调用该方法后会触发 DoWork 事件,并且为处理 DoWork 事件的函数 DoWorkEventArg 事件

参数,其中包含了 RunWorkerAsync 传递的参数。在相应 DoWork 的处理函数中就可以做具体的复杂操作。

BackgroundWorker.ReportProgress—— 有时候需要在一个冗长的操作中向用户不断反馈进度,这样的话就可 以调用的 ReportProgress(int percent),在调用 ReportProgress 方法时,触 发 ProgressChanged 事件。提供一个在 0 到 100 之间的整数,它表示后台活动 已完成的百分比。你也可能提供任何对象作为第二个参数,允许你 给事件处理 程序传递状态信息。作为传递到此过程的 ProgressChangedEventArgs 参数属 性, 百分比和你自己的对象 (如果提供的话) 均要被传递到 ProgressChanged 事 件处理程序。这些属性被分别命名为 ProgressPercentage 和 UserState,并且 你的事件处理程序可以以任何需要的方式使用它们。(注意:只有在 BackgroundWorker.WorkerReportsProgress 属性被设置为 true 该方法才可用)。

BackgroundWorker.CancelAsync—— 但需要退出异步调用的时候,就调用的这个方法。但是样还不够,因为它仅 仅是将 BackgroudWorker.CancellationPending 属 性设置为 true。你需要在具 体的异步调用处理的时候,不断检查 BackgroudWorker.CancellationPending 是否为 true,如果是真的话就退出。(注意:只有在 BackgroundWorker.WorkerSupportsCancellation 属性被设置为 true 该方法才 可用)。

具体代码示例可以在博客中找到!

示例代码下载地址http://files.cnblogs.com/dudg/BackgroudWokerUI.rar

上一篇:[转]Centos 7搭建Gitlab服务器超详细


下一篇:setjmp和longjmp的使用