深入剖析BackgroundWorker类

BackgroundWorker是一个在System.ComponentModel命名空间下的帮助类,用于管理工作线程。它提供了以下几个重要的特性:

1)“cancel”标记,可以在不使用Abort的情况下给工作线程打信号终止工作线程(调用CancelAsync方法)。

2)提供报告进度、完成度和退出的标准方案

3)实现了IComponet接口,允许它参与Visual Studio设计器:可以直接从工具箱中拖出而不必写代码进行实例化

4)在工作线程上做异常处理

5)更新Windows Forms 控件以应答工作进度或完成度的能力

其中特性4)和特性5)两条尤为有用,这些使我们不需要再在工作线程中使用try/catch块,并且更新UI控件不需要再调用Control.Invoke了。

实际上BackgroundWorker是使用线程池工作的,这意味着不能在BackgroundWorker线程上调用Abort方法。

好了,说了一大堆概念,下面来看看BackgroundWorker是怎样工作的。

1)首先,调用BackgroundWorker实例(这里假定该实例名为backgroundWorker1)的RunWorkerAsync方法来开启后台线程。BackgroundWorker使用十分方便,调用RunWorkerAsync后程序会自动完成接下来的工作直到后台线程完成任务退出,除非用户想取消后台线程或者异常退出。

RunWorkerAsync有两种重载方式:void RunWorkerAsync()和RunWorkerAsync(object argument)。如果不需要给后台线程传入初始数据,使用第一种重载方式就可以了;而当后台线程需要初始数据时就可以使用方式二,不过该重载只接受一个参数,所以如果有多个数据需要传入就要考虑封装成结构或类。

2)调用RunWorkerAsync会触发BackgroundWorker的DoWork事件,RunWorkerAsync(object argument)提供的参数也会传给DoWork的委托方法。DoWork事件的委托方法的形式为【函数名(object sender, DoWorkEventArgs e)】,如:

backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)

变量e有两个属性:Argument和Result,RunWorkerAsync传入的参数argument就传给了DoWorkEventArgs e的Argument属性;而另一个属性Result则是保存运算结果,二者都是object类型。可以将运算结果赋给Result属性,这个属性会在运算完成后传给OnRunWorkerCompleted事件的委托方法的参数RunWorkerCompletedEventArgs e,从而将运算结果带给前台线程。

DoWork事件的委托方法主要完成后台处理。需要注意的是该委托方法中在处理数据前要判断是否已请求取消后台操作(如调用CancelAsync了方法),如果已经请求取消后台操作,则退出后台线程。判断是否请求取消后台线程的方法为CancellationPending。

if (backgroundWorker1.CancellationPending)

{

e.Cancel=true;

return ;

}

处理过程中不断发送处理信息,以供前台显示处理进度或完成度的操作也是在这个委托中完成的。发送信息的是BackgroundWorker类的ReportProgress方法[前提是BackgroundWorker的WorkerReportsProgress属性已经设为true,否则调用该方法会引发异常]。ReportProgress也有两种重载形式:void ReportProgress(int percentProgress)和void ReportProgress(int percentProgress, object userState)。

percentProgress返回的是已完成的后台操作所占的百分比,范围从 0% 到 100%。由于是百分比,所以最好是一个[0,100]闭合区间的整数,如果超出这个范围则可能引发异常(例如把这个数据赋给进度条控件时)。

关于userState参数,微软给出的说法是:传递到 BackgroundWorker.RunWorkerAsync(System.Object)的状态对象。我感觉是发送后台处理状态给ProgressChanged事件,以供前台判断后续操作,当然也可以把处理过程中的数据通过这个参数传给前台显示。

需要注意的是:在ReportProgress发送信息之前或之后,后台线程应该交出时间片,以供其他线程完成相关操作,如主线程UI界面的更新操作等,否则可能会使其他线程无法工作甚至使程序死掉。交出时间片的工作可以使用Thread.Sleep()方法完成,使后台线程暂时休眠一段时间。休眠时间长短可以根据UI线程更新需要的时间设定[如果太短,有可能导致UI线程的更新操作无法完成],如Thread.Sleep(100)。当然如果仅仅是为了交出时间片,也可以设成Thread.Sleep(0)。

3)ReportProgress方法会触发BackgroundWorker的ProgressChanged事件。ProgressChanged事件的委托方法在主线程中运行,主要用于将处理进度等信息反馈给主线程[DoWork事件的委托方法不负责这项工作]。ProgressChanged的委托方法形式为:

【方法名(object sender, ProgressChangedEventArgs e)】,如下:

backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)

参数e中就包含着ReportProgress传出的percentProgress和userState[如果第二步调用的是ReportProgress的只有一个参数的重载形式,那么e.UserState为空]。

就这样,BackgroundWorker进行后台运算并和主线程界面进行通信,直到后台任务完成或被中断。

对于一个友好的程序,当后台工作完成后,还应该给用户以提示工作已经完成。这项工作可以通过在BackgroundWorker的OnRunWorkerCompleted事件中添加代码或为其设置委托方法来完成。OnRunWorkerCompleted事件的委托方法形式为【函数名(object sender, RunWorkerCompletedEventArgs e)】。前面说过DoWork事件的委托方法的参数DoWorkEventArgs e中有一个属性Result用于保存运算结果,这个属性的值会在后台运算完成后自动传到OnRunWorkerCompleted事件的委托方法中,从而传给前台线程进行显示。需要注意的是,应该在该方法中判断线程结束的原因[完成后台操作/取消后台操作/发生异常错误],只有成功完成后台操作的情况下才能使用e.Result;如果e.Cancel==true或者e.Error!=null就不能使用e.Result,否则会引发“System.InvalidOperationException”类型的异常。

BackgroundWorker还提供了CancelAsync方法允许用户手动终止后台任务,正常使用该方法的前提是WorkerSupportsCancellation属性为true。

至此,关于BackgroundWorker的介绍基本介绍完毕了,希望能给您带来帮助!下面给出一个小例子,展示了BackgroundWorker类的基本使用。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

namespace BackgroundWorkerTest

{

    public partial class Form1 : Form

    {

        public Form1()

        {

            InitializeComponent();

            backgroundWorker1.WorkerReportsProgress = true;//报告完成进度

            backgroundWorker1.WorkerSupportsCancellation = true;//允许用户终止后台线程

        }

        //开始按钮

        private void button1_Click(object sender, EventArgs e)

        {

            if (!backgroundWorker1.IsBusy )//判断backgroundWorker1是否正在运行异步操作

            {

                //backgroundWorker1.RunWorkerAsync();

                backgroundWorker1.RunWorkerAsync(1000);//开始执行后台操作

            }

        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)

        {

            e.Result = ListNumber(backgroundWorker1, e);//运算结果保存在e.Result中

        }

        bool ListNumber(object sender, DoWorkEventArgs e)

        {

            int num=(int)e.Argument;//接收传入的参数

            for (int i = 1; i <= num; i++)

            {

                if (backgroundWorker1.CancellationPending)//判断是否请求了取消后台操作

                {

                    e.Cancel=true;

                    return false;

                }

                //backgroundWorker1.ReportProgress(i * 100 / num);

                backgroundWorker1.ReportProgress(i * 100 / num,i);//报告完成进度

                Thread.Sleep(0);//后台线程交出时间片

            }

            return true;

        }

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)

        {

            //将完成进度数据传给进度条

            progressBar1.Value = e.ProgressPercentage;

            label1.Text = e.ProgressPercentage + "%";

            //将中间计算结果在ListBox控件中显示出来

            listBox1.Items.Add(e.UserState);

        }

     Private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

     {

          if (!e.Cancelled && e.Error==null)

          {

              MessageBox.Show("处理完成了吗?  " + e.Result);

          }

          else//如果取消后台线程或发生了异常

          {

              MessageBox.Show("处理完成了吗?  false");

          }

      }

        //取消按钮

        private void button2_Click(object sender, EventArgs e)

        {

            if (backgroundWorker1.WorkerSupportsCancellation==true)

            {

                backgroundWorker1.CancelAsync();//取消后台操作

                backgroundWorker1.Dispose();//释放资源

            }

        }

    }

}

运行结果如下图:

时间: 2024-10-11 02:19:08

深入剖析BackgroundWorker类的相关文章

C#多线程 BackgroundWorker类使用小例-WPF程序

1.程序实现了一个简单的使用了BackgroundWorker类的WPF程序,用于在后台线程给进度条赋值. 运行结果如下: 后台线程正常运行结束: 后台线程中途被取消: 2.程序仅修改了 MainWindow.xaml 文件和 MainWindow.xaml.cs 文件,两个文件内容如下 MainWindow.xaml 文件: <Window x:Class="SimpleWorker.MainWindow" xmlns="http://schemas.microsof

thread学习笔记--BackgroundWorker 类

背景: 在 WinForms 中,有时要执行耗时的操作,比如统计某个磁盘分区的文件夹或者文件数目,如果分区很大或者文件过多的话,处理不好就会造成“假死”的情况,或者报“线程间操作无效”的异常,或者在该操作未完成之前操作用户界面,会导致用户界面停止响应. ----比如现在学习的MES UI中要查询数据量较大的记录显示在Spread中就可以用BackgroundWorker 类! 解决的方法就是新开一个线程,把耗时的操作放到线程中执行,这样就可以在用户界面上进行其它操作. 如果不借助Thread编程

C#多线程 BackgroundWorker类使用小例

1. 示例程序 是一个控制台应用程序,程序实现了分别在主线程和后台线程并行执行,达到为数组赋值的效果. 可以中途停止后台线程. 2. 执行结果图片如下: 正常执行结束: 中途停止后台线程: 3.代码 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel; using System.Threading; namespace B

Thread类源码剖析

目录 1.引子 2.JVM线程状态 3.Thread常用方法 4.拓展点 一.引子 说来也有些汗颜,搞了几年java,忽然发现竟然没拜读过java.lang.Thread类源码,这次特地拿出来晒一晒.本文将剖析Thread类源码(本文后面源码全部默认JDK8),并讲解一些重要的拓展点.希望对大家能有一些帮助. 本文讲解主干全部出自源码和注释,保证了权威性.(注意:网上,某些书中很多观点都是错的,过时的,片面的,所以大家一定要看源码,重要事情说N遍,看源码!看源码!看源码......) 二.JVM

BackgroundWorker原理剖析

BackgroundWorker类位于System.ComponentModel命名空间下,主要用来异步执行一个长时间的操作,然后,在完成事件中安全更新UI的控件属性.UI中的控件是不允许非创建该控件的线程修改的.典型用法如下: BackgroundWorker m_worker = new BackgroundWorker(); // 设置支持进度报告.异步取消功能,默认都为false m_worker.WorkerReportsProgress = true; m_worker.Worker

4.3 用户自定义类

4.3.1 Employee类 package class_; import java.util.Date; import java.util.GregorianCalendar; public class EmployeeTest { public static void main(String args[]) { Employee[] staff = new Employee[3]; staff[0] = new Employee("Carl Cracker", 75000, 19

【翻译】C#中使用BackgroundWorker实现多线程

原文地址:MultiThreading Using a Background Worker, C# 介绍 当开发Windows Forms应用程序时,你会常常注意到:当执行某个耗时的操作,比如处理一个打文件或是从远程服务器请求数据 ,用户界面会进入假死状态.这是由于你的应用程序是运行在单线程下.这个线程负责响应用户界面的操作,同时也负责处理应用程序中所有的事件和方法.因此,耗时的操作会阻塞你的用户界面,直到操作完成.今天,我们将要做的是把这些耗时的操作移到另一个不同的线程中,当以在另一边执行操作

【Struts2】剖析Struts2中的反射技术 ValueStack(值栈)

1,Struts2框架主要组件的处理流程 在说ValueStack之前,笔者先说一说Struts2中常用的组件,struts2中常用组件有strutsPrepareAndExecuteExceptionn,以及一般执行流程: 请求来进入 Filter 控制器 Filter 控制器创建 ValueStack 对象并初始化 Filter 控制器根据 struts.xml 调用 defaultStack 拦截器栈 Filter 控制器根据 struts.xml 调用 Action 处理 Filter

BackgroundWorker与线程使用

在一个程序中,一些耗时的操作,在长时间运行时可能会导致用户界面 (UI) 处于停止响应状态,用户在这操作期间无法进行其他的操作,为了不使UI层处于停止响应状态,需要将这些耗时的操作,设置为后台线程,并且在UI层可以展示后台操作的进度,比较常用的方法有使用线程以及BackgroundWorker类. 1.线程方法 当我们要在前太展示后台的操作进程时,是不允许跨线程访问控件.此时需要取消控件的跨线程访问,在winform中可以设置CheckForIllegalCrossThreadCalls = f