在VS2005中添加了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的各属性、方法、事件的调用机制和顺序:
从上图可见在整个生活周期内发生了3次重要的参数传递过程:
参数传递1:此次的参数传递是将RunWorkerAsync(Object)中的Object传递到DoWork事件的DoWorkEventArgs.Argument,由于在这里只有一个参数可以传递,所以在实际应用往封装一个类,将整个实例化的类作为RunWorkerAsync的Object传递到DoWorkEventArgs.Argument;
参数传递2:此次是将程序运行进度传递给ProgressChanged事件,实际使用中往往使用给方法和事件更新进度条或者日志信息;
参数传递3:在DoWork事件结束之前,将后台线程产生的结果数据赋给DoWorkEventArgs.Result一边在RunWorkerCompleted事件中调用RunWorkerCompletedEventArgs.Result属性取得后台线程产生的结果。
另外从上图可以看到DoWork事件是在后台线程中运行的,所以在该事件中不能够操作用户界面的内容,如果需要更新用户界面,可以使用ProgressChanged事件及RunWorkCompleted事件来实现。
明白了BagkgroundWorker的事件调用顺序和参数传递机制之后在使用该组件用于多线程编程的时候就可以轻松许多了。详细的实例可以在我写的天涯离线浏览器中看到。
-------------
这是一个在.net2.0里面新出现的类,用于执行后台比较长的任务而又想能和UI有点操作的应用里面。
普通情况下,你点击一个按钮,去后台执行一个process,如果你想得到结果,就得等这个process结束。通常,可以使用异步执行回调来解决这个问题。现在,backgroundworker给我们实现了这样一种简单的封装,可以把我们的复杂任务交给新的线程去处理,然后继续UI线程。等到我们的任务需要通知UI做什么事情的时候,可以report一下,在其事件里就可以直接使用UI控件,而不需要Control.Invoke去掉用之。
有这样一个应用:客户需要把大量数据(需要执行3天)copy到另外一个机器,中间想能看到有多少数据被复制/失败等(实时报道)。
在这个例子里面,我们的界面可能非常简单:一个开始按钮,一个结束按钮,一个richtextBox来显示运行记录。但是后台执行可能就会比较棘手。如果简单的执行,并且报告,那么整个界面将失去响应(都在同一个线程里面,造成忙碌)。这时候,可以使用这个backgroundworker了。它可以在后台执行,并且报告给界面实时信息,界面不会失去响应。
先介绍一下backgroundworker的几个属性/方法
.WorkerReportsProgress:是否可以向外报告进度。
.WorkerSupportsCancellation :是否可以暂停任务
. CancellationPending: 是否正在暂停中
. RunWorkerAsync() : 开始执行任务。触发DoWork事件
. ReportProgress(int percentPrgress,object userState) : 向外报告进度。触发ProgressChanged事件.其中,参数可以在ProgressChangedEventArgs(worker_ProgressChanged(object sender, ProgressChangedEventArgs e))中得到
. CancelAsync() :取消(暂停)执行。
事件
worker.DoWork += new DoWorkEventHandler(worker_DoWork);//执行任务
worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);//任务结束时
worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged)//报告状态
按照上边的资料,我们这个应用就可以这样处理之
formDisplay是用于显示实时状态的窗口。有DisplyMessage方法来显示信息到界面
在Hanlder类(处理文件copy的)里面:
static void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//show the message on windows
formDisplay.DisplyMessage(“copy”, e.UserState.ToString());//show message.
}
static void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
string msg = "JOB copy : have been completed";
formDisplay.DisplyMessage(msg);//show message
}
static void worker_DoWork(object sender, DoWorkEventArgs e)
{
for(…)
{
//copying
(sender as BackgroundWorker). ReportProgress(0,”xxx complete”);//report
}
}
这样构造的程序里面,才不会出现UI失去响应。
当然,通过写自己的异步处理也可以实现,功能更强大。只不过这个用起来更简单。
至于backgroundworker是怎么实现的呢?这里有人已经做出了一些解答:
http://blog.joycode.com/sunmast/archive/2006/03/02/about_system_componentmodel_asyncoperation.aspx