微软提供了一个快捷使用多线程的帮助类BackgroundWorker,能够快速创建一个新的线程,并能报告进度,暂停,以及在线程完成后处理别的任务。

1.BackgroundWorker类介绍

1.1. 四个常用属性:

      public bool IsBusy { get; }    //只读属性,用来判断当前线程是否正在工作中

    public bool WorkerReportsProgress { get; set; }   //决定当前线程是否能报告进度

    public bool WorkerSupportsCancellation { get; set; }   //决定当前线程能否取消

public bool CancellationPending { get; }    //只读属性,用来判断是否发送了取消线程的消息(当调用CancelAsync()方法时,被设置为true)

1.2. 三个常用事件:

    public event DoWorkEventHandler DoWork; //开始 必须,线程的主要逻辑,调用RunWorkerAsync()时触发该事件

    public event ProgressChangedEventHandler ProgressChanged; //报告 可选,报告进度事件,调用ReportProgress()时触发该事件

    public event RunWorkerCompletedEventHandler RunWorkerCompleted; //结束 可选,当线程运行完毕、发生异常和调用CancelAsync()方法这三种方式都会触发该事件

1.3. 三个常用方法:

    public void RunWorkerAsync(); //启动线程,触发DoWork事件
    public void RunWorkerAsync(object argument);

    public void ReportProgress(int percentProgress); //报告进度,触发ProgressChanged事件
    public void ReportProgress(int percentProgress, object userState);

    public void CancelAsync(); //取消线程,将CancellationPending设置为true

2.BackgroundWorker用法

2.1. 简单用法:

新建BackgroundWorder对象;

根据需求, 设置是否能取消(WorkerSupportsCancellation)、是否报告进度(WorkerReportsProgress);

根据需求,设置好相关事件,DoWorker、ProgressChanged、ProgressChanged;

调用RunWorkerAsyns()方法,启动线程;

在需要取消的位置,判断CancellationPending的值,并做相关处理;//可选

在适当的位置调用ReportProgress(int percentProgress)方法,报告进度。

2.2.简单例子:

2.2.1. 最基本的运行代码

界面的简单代码:

界面上就一个Grid控件和2个Button

 1 <Window x:Class="BackgroundWorkerDemo20170324.MainWindow"
 2         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 3         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 4         Title="MainWindow" Height="342" Width="504">
 5     <Grid>
 6         <ProgressBar x:Name="pBar" Margin="50,69,43,209">
 7         </ProgressBar>
 8         <Button x:Name="btnStart" Content="start" Click="btnStart_Click" Margin="50,218,311,63"/>
 9         <Button x:Name="btnCancel" Content="cancel" Click="btnCancel_Click" Margin="285,218,95,63"/>
10     </Grid>
11 </Window>

View Code

后台代码:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.ComponentModel;
 4 using System.Linq;
 5 using System.Text;
 6 using System.Threading;
 7 using System.Windows;
 8 using System.Windows.Controls;
 9 using System.Windows.Data;
10 using System.Windows.Documents;
11 using System.Windows.Input;
12 using System.Windows.Media;
13 using System.Windows.Media.Imaging;
14 using System.Windows.Navigation;
15 using System.Windows.Shapes;
16
17 namespace BackgroundWorkerDemo20170324
18 {
19     /// <summary>
20     /// Interaction logic for MainWindow.xaml
21     /// </summary>
22     public partial class MainWindow : Window
23     {
24         private BackgroundWorker worker;
25         public MainWindow()
26         {
27             InitializeComponent();
28             worker = new BackgroundWorker();                      //新建BackgroundWorker
29             worker.WorkerReportsProgress = true;                  //允许报告进度
30             worker.WorkerSupportsCancellation = true;             //允许取消线程
31             worker.DoWork += worker_DoWork;                       //设置主要工作逻辑
32             worker.ProgressChanged += worker_ProgressChanged;     //进度变化的相关处理
33             worker.RunWorkerCompleted += worker_RunWorkerCompleted;  //线程完成时的处理
34         }
35
36         /// <summary>
37         /// 主要工作逻辑
38         /// </summary>
39         /// <param name="sender"></param>
40         /// <param name="e"></param>
41         private void worker_DoWork(object sender, DoWorkEventArgs e)
42         {
43             BackgroundWorker tempWorker = sender as BackgroundWorker;
44             for (int i = 0; i <= 100; i++)
45             {
46                 Thread.Sleep(200);    //避免太快,让线程暂停一会再报告进度
47                 tempWorker.ReportProgress(i);   //调用ReportProgress()方法报告进度,同时触发ProgressChanged事件
48             }
49         }
50
51         /// <summary>
52         /// 处理进度变化,改变进度条的值
53         /// </summary>
54         /// <param name="sender"></param>
55         /// <param name="e"></param>
56         private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
57         {
58             pBar.Value = e.ProgressPercentage;
59         }
60
61         /// <summary>
62         /// 线程完成后的处理
63         /// </summary>
64         /// <param name="sender"></param>
65         /// <param name="e"></param>
66         private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
67         {
68             MessageBox.Show("线程工作完成");
69         }
70
71         /// <summary>
72         /// 点击Start按钮启动线程
73         /// </summary>
74         /// <param name="sender"></param>
75         /// <param name="e"></param>
76         private void btnStart_Click(object sender, RoutedEventArgs e)
77         {
78             worker.RunWorkerAsync();   //调用该方法才会启动线程
79         }
80
81         /// <summary>
82         /// 点击Cancel按钮取消线程,但先判断线程是否正在工作
83         /// </summary>
84         /// <param name="sender"></param>
85         /// <param name="e"></param>
86         private void btnCancel_Click(object sender, RoutedEventArgs e)
87         {
88             if (worker.IsBusy)
89                 worker.CancelAsync();
90             else
91                 MessageBox.Show("There is no thead running now.");
92         }
93     }
94 }

View Code

2.2.2. 能取消线程

在需要取消线程的位置判断CancellationPending属性,一般在循环体中(因为循环一般耗时居多),判断当CancellationPending==true时,

  需要将DoWorkEventArgs的Cancel属性设置为true, 然后就可以在RunWorkerCompleted中判断RunWorkerCompletedEventArgs的Cancelled

  属性来进行对应的处理(即,当用户取消线程时,也会触发RunWorkerCompleted事件)

修改后的代码:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Linq;
  5 using System.Text;
  6 using System.Threading;
  7 using System.Windows;
  8 using System.Windows.Controls;
  9 using System.Windows.Data;
 10 using System.Windows.Documents;
 11 using System.Windows.Input;
 12 using System.Windows.Media;
 13 using System.Windows.Media.Imaging;
 14 using System.Windows.Navigation;
 15 using System.Windows.Shapes;
 16
 17 namespace BackgroundWorkerDemo20170324
 18 {
 19     /// <summary>
 20     /// Interaction logic for MainWindow.xaml
 21     /// </summary>
 22     public partial class MainWindow : Window
 23     {
 24         private BackgroundWorker worker;
 25         public MainWindow()
 26         {
 27             InitializeComponent();
 28             worker = new BackgroundWorker();                      //新建BackgroundWorker
 29             worker.WorkerReportsProgress = true;                  //允许报告进度
 30             worker.WorkerSupportsCancellation = true;             //允许取消线程
 31             worker.DoWork += worker_DoWork;                       //设置主要工作逻辑
 32             worker.ProgressChanged += worker_ProgressChanged;     //进度变化的相关处理
 33             worker.RunWorkerCompleted += worker_RunWorkerCompleted;  //线程完成时的处理
 34         }
 35
 36         /// <summary>
 37         /// 主要工作逻辑
 38         /// </summary>
 39         /// <param name="sender"></param>
 40         /// <param name="e"></param>
 41         private void worker_DoWork(object sender, DoWorkEventArgs e)
 42         {
 43             BackgroundWorker tempWorker = sender as BackgroundWorker;
 44             for (int i = 0; i <= 100; i++)
 45             {
 46                 if (tempWorker.CancellationPending)  //当点击Cancel按钮时,CancellationPending被设置为true
 47                 {
 48                     e.Cancel = true;  //此处设置Cancel=true后,就可以在RunWorkerCompleted中判断e.Cancelled是否为true
 49                     break;
 50                 }
 51                 Thread.Sleep(200);    //避免太快,让线程暂停一会再报告进度
 52                 tempWorker.ReportProgress(i);   //调用ReportProgress()方法报告进度,同时触发ProgressChanged事件
 53             }
 54         }
 55
 56         /// <summary>
 57         /// 处理进度变化,改变进度条的值
 58         /// </summary>
 59         /// <param name="sender"></param>
 60         /// <param name="e"></param>
 61         private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
 62         {
 63             pBar.Value = e.ProgressPercentage;
 64         }
 65
 66         /// <summary>
 67         /// 线程完成后的处理
 68         /// </summary>
 69         /// <param name="sender"></param>
 70         /// <param name="e"></param>
 71         private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
 72         {
 73             if (e.Cancelled)  //被取消时
 74                 MessageBox.Show("线程被取消了");
 75             else              //正常结束
 76                 MessageBox.Show("线程工作完成");
 77         }
 78
 79         /// <summary>
 80         /// 点击Start按钮启动线程
 81         /// </summary>
 82         /// <param name="sender"></param>
 83         /// <param name="e"></param>
 84         private void btnStart_Click(object sender, RoutedEventArgs e)
 85         {
 86             worker.RunWorkerAsync();   //调用该方法才会启动线程
 87         }
 88
 89         /// <summary>
 90         /// 点击Cancel按钮取消线程,但先判断线程是否正在工作
 91         /// </summary>
 92         /// <param name="sender"></param>
 93         /// <param name="e"></param>
 94         private void btnCancel_Click(object sender, RoutedEventArgs e)
 95         {
 96             if (worker.IsBusy)
 97                 worker.CancelAsync();
 98             else
 99                 MessageBox.Show("There is no thead running now.");
100         }
101     }
102 }

View Code

3.线程中的传值

3.1. 与BackgroundWorker相关的三个参数类

    //// 摘要: //     引发 System.ComponentModel.BackgroundWorker.DoWork 事件。//// 参数: //   e://     包含事件数据的 System.EventArgs。protected virtual void OnDoWork(DoWorkEventArgs e);//// 摘要: //     引发 System.ComponentModel.BackgroundWorker.ProgressChanged 事件。//// 参数: //   e://     包含事件数据的 System.EventArgs。protected virtual void OnProgressChanged(ProgressChangedEventArgs e);//// 摘要: //     引发 System.ComponentModel.BackgroundWorker.RunWorkerCompleted 事件。//// 参数: //   e://     包含事件数据的 System.EventArgs。protected virtual void OnRunWorkerCompleted(RunWorkerCompletedEventArgs e);

通过上面的代码我们可以看到,BackgroundWorker的三个常用事件都有与之对应的参数类:

3.1.1. DoWorkEventArgs 为DoWork事件提供数据,详细代码如下:

 1 #region 程序集 System.dll, v2.0.0.0
 2 // C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll
 3 #endregion
 4
 5 using System;
 6
 7 namespace System.ComponentModel
 8 {
 9     // 摘要:
10     //     为 System.ComponentModel.BackgroundWorker.DoWork 事件处理程序提供数据。
11     public class DoWorkEventArgs : CancelEventArgs
12     {
13         // 摘要:
14         //     初始化 System.ComponentModel.DoWorkEventArgs 类的新实例。
15         //
16         // 参数:
17         //   argument:
18         //     指定异步操作的参数。
19         public DoWorkEventArgs(object argument);
20
21         // 摘要:
22         //     获取表示异步操作参数的值。
23         //
24         // 返回结果:
25         //     表示异步操作参数的 System.Object。
26         public object Argument { get; }
27         //
28         // 摘要:
29         //     获取或设置表示异步操作结果的值。
30         //
31         // 返回结果:
32         //     表示异步操作结果的 System.Object。
33         public object Result { get; set; }
34     }
35 }

View Code

     只有两个object类型的属性Argument(只读的)和Result,

因为调用RunWorkerAsync()方法,就会触发DoWork事件,细心的你会发现,RunWorkerAsync()方法有两个重载,

带有参数的RunWorkerAsync(object argument),这个形参argument就是传递给DoWorkEventArgs的Arument属性的

而Result属性是作为DoWork事件的结果传递给RunWorkerCompletedEventArgs的Result属性,

3.1.2.  ProgressChangedEventArgs 为ProgressChanged事件提供数据,详细代码如下:

 1 using System;
 2
 3 namespace System.ComponentModel
 4 {
 5     // 摘要:
 6     //     为 System.ComponentModel.BackgroundWorker.ProgressChanged 事件提供数据。
 7     public class ProgressChangedEventArgs : EventArgs
 8     {
 9         // 摘要:
10         //     初始化 System.ComponentModel.ProgressChangedEventArgs 类的新实例。
11         //
12         // 参数:
13         //   progressPercentage:
14         //     已完成的异步任务的百分比。
15         //
16         //   userState:
17         //     唯一的用户状态。
18         public ProgressChangedEventArgs(int progressPercentage, object userState);
19
20         // 摘要:
21         //     获取异步任务的进度百分比。
22         //
23         // 返回结果:
24         //     指示异步任务进度的百分比值。
25         public int ProgressPercentage { get; }
26         //
27         // 摘要:
28         //     获取唯一的用户状态。
29         //
30         // 返回结果:
31         //     指示用户状态的唯一 System.Object。
32         public object UserState { get; }
33     }
34 }

View Code

也只有两个属性,都是只读的,一个为int类型的ProgerssPercentage,表示任务进度百分百;另一个是object类型的UserState

这两个参数都是通过ReportProgress()方法传入,由于UserState属性时object类型的,所以当需要实现复制逻辑时,可以自定义一个类型

3.1.3. RunWorkerCompletedEventArgs 稍微特殊一点,虽然该类的直接定义中只有两个属性,

object类型的两个只读属性Result和UserState

 1 namespace System.ComponentModel
 2 {
 3     public class RunWorkerCompletedEventArgs : AsyncCompletedEventArgs
 4     {
 5         public RunWorkerCompletedEventArgs(object result, Exception error, bool cancelled);
 6
 7         public object Result { get; }
 8         [Browsable(false)]
 9         [EditorBrowsable(EditorBrowsableState.Never)]
10         public object UserState { get; }
11     }
12 }

View Code

但从父类继承过来的Cancelled和Error属性才是重点

 1 namespace System.ComponentModel
 2 {
 3     public class AsyncCompletedEventArgs : EventArgs
 4     {
 5         public AsyncCompletedEventArgs(Exception error, bool cancelled, object userState);
 6
 7         [SRDescription("Async_AsyncEventArgs_Cancelled")]
 8         public bool Cancelled { get; }
 9         [SRDescription("Async_AsyncEventArgs_Error")]
10         public Exception Error { get; }
11         [SRDescription("Async_AsyncEventArgs_UserState")]
12         public object UserState { get; }
13
14         protected void RaiseExceptionIfNecessary();
15     }
16 }

View Code

所以,RunWorkerCompletedEventArgs参数主要关心三个属性,ErrorCancelledResult (UserState一般很少用到)

注意:标准的RunWorkerCompleted处理都应该先处理Error和Cancelled情况,否则直接访问Result时会报错,

尽量以这种方式来整理自己的代码逻辑:

 1 // This event handler deals with the results of the
 2 // background operation.
 3 private void backgroundWorker1_RunWorkerCompleted(
 4     object sender, RunWorkerCompletedEventArgs e)
 5 {
 6     // First, handle the case where an exception was thrown.
 7     if (e.Error != null)
 8     {
 9         MessageBox.Show(e.Error.Message);
10     }
11     else if (e.Cancelled)
12     {
13         // Next, handle the case where the user canceled
14         // the operation.
15         // Note that due to a race condition in
16         // the DoWork event handler, the Cancelled
17         // flag may not have been set, even though
18         // CancelAsync was called.
19         resultLabel.Text = "Canceled";
20     }
21     else
22     {
23         // Finally, handle the case where the operation
24         // succeeded.
25         resultLabel.Text = e.Result.ToString();
26     }
27 }

View Code

4.注意事项

4.1. DoWork事件中不能与UI控件进行交流

如果需要在线程处理过程中与UI控件进行交流,请在ProgressChanged和RunWorkerCompleted中进行,否则会出现以下错误

System.InvalidOperationException was unhandled by user codeMessage=The calling thread cannot access this object because a different thread owns it.Source=WindowsBaseStackTrace:at System.Windows.Threading.Dispatcher.VerifyAccess()at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value)at System.Windows.Controls.Primitives.RangeBase.set_Value(Double value)at BackgroundWorkerDemo20170324.MainWindow.worker_DoWork(Object sender, DoWorkEventArgs e) in 

5.附带

5.1. Visual Studio中的API显示如下:

代码:

 1 using System;
 2
 3 namespace System.ComponentModel
 4 {
 5     [DefaultEvent("DoWork")]
 6     public class BackgroundWorker : Component
 7     {
 8         public BackgroundWorker();
 9
10         [Browsable(false)]
11         public bool CancellationPending { get; }                          //用于判断是否取消了线程
12         [Browsable(false)]
13         public bool IsBusy { get; }                                       //判断当前线程是否正在工作
14         [DefaultValue(false)]
15         public bool WorkerReportsProgress { get; set; }                   //是否报告进度
16         [DefaultValue(false)]
17         public bool WorkerSupportsCancellation { get; set; }              //是否支持取消线程,如果要调用CancelAsyns()方法,必须设置为true
18
19         public event DoWorkEventHandler DoWork;                           //线程核心代码,耗时的操作放在这里,调用RunWorkerAsync()时触发
20         public event ProgressChangedEventHandler ProgressChanged;         //当进度改变后执行的代码,调用ReportProgress()时触发
21         public event RunWorkerCompletedEventHandler RunWorkerCompleted;   //3种情况:当线程完成、手动取消线程时、线程发生异常时触发
22
23         public void CancelAsync();                                        //调用该方法会发出取消线程的消息,但并不会立即中止线程
24         protected virtual void OnDoWork(DoWorkEventArgs e);
25         protected virtual void OnProgressChanged(ProgressChangedEventArgs e);
26         protected virtual void OnRunWorkerCompleted(RunWorkerCompletedEventArgs e);
27         public void ReportProgress(int percentProgress);                  //调用该方法就触发ProgressChanged事件,相当于ReportProgress(int percentProgress, null)
28         public void ReportProgress(int percentProgress, object userState);
29         public void RunWorkerAsync();                                     //调用该方法启动线程,同时触发DoWork事件,相当于RunWorkerAsync(null)
30         public void RunWorkerAsync(object argument);
31     }
32 }

View Code

5.2. 相关博客推荐,非常优秀,值得查看:

[你必须知道的异步编程]——基于事件的异步编程模式 ;

http://m.blog.csdn.net/article/details?id=7291070

BackgroundWorker简单实用(简便的异步操作)相关推荐

  1. 分享几个用 Python 给图片添加水印的方法,简单实用

    作者 |俊欣 来源 |关于数据分析与可视化 今天来分享几种可以给图片添加水印的方法,都是十分的简单实用,大家在看了之后也可以私底下去自己试试,有些方法需要的代码量就比较少,有些方法需要的代码量就稍微多 ...

  2. 【Python】分享几个用Python给图片添加水印的方法,简单实用

    今天来分享几种可以给图片添加水印的方法,都是十分的简单实用,大家在看了之后也可以私底下去自己试试,有些方法需要的代码量就比较少,有些方法需要的代码量就稍微多一些,那我们开始吧 opencv模块 首先我 ...

  3. 太妙了!几个用Python给图片添加水印的方法,简单实用!

    今天来分享几种可以给图片添加水印的方法,都是十分的简单实用,大家在看了之后也可以私底下去自己试试,有些方法需要的代码量就比较少,有些方法需要的代码量就稍微多一些,那我们开始吧 opencv模块 首先我 ...

  4. 用Python给图片添加水印的3种方法,简单实用

    来源:关于数据分析与可视化 今天来分享几种可以给图片添加水印的方法,都是十分的简单实用,大家在看了之后也可以私底下去自己试试,有些方法需要的代码量就比较少,有些方法需要的代码量就稍微多一些,那我们开始 ...

  5. 一个简单实用的,基于EF的三层架构

    到底什么样的框架才是好框架呢?或许不同人有不同的看法.我个人觉一个好的框架,最重要的要是简单实用,能快速适开发,可维护性高(不会出现复制黏贴的代码),并能快速响应各种业务场景的变化的框架,同时性能不会 ...

  6. html 可调节进度条控件,jQuery简单实用的轻量级进度条插件

    jQMeter是一款简单实用的轻量级进度条jQuery插件,它可以显示为水平或垂直进度条,进度条加载时带有动画特效,你只需要简单的传入一些参数到jQMeter对象的构造函数中就可以完成你想要的进度条效 ...

  7. matlab简单程序实例_visual basic VB.NET实例系列教程第一节(简单实用抽奖程序)...

    近期疫情原因,工作比较不忙,所以打算出一套零基础,VB.NET实例系列入门教程,实用又好玩,带大家进入VB的编程世界里,希望这套图文教程能帮到有需要的人! 第一节(简单实用抽奖程序) 内容准备:编译环 ...

  8. 用aspnetpager实现datalist分页(绝对的简单实用)

    微软的Datalist在做电子相册时候必不可少,但是不支持分页功能,都出到2008了还是没有分页功能,幸好网上有个专业的分页空间aspnetpager,帮我们解决了大问题,说实在话的,网上关于data ...

  9. 这两天老是有兄弟问到Vue的登陆和注册,登陆成功留在首页,没有登录回到登录页面,现在我用最简单实用的方法实现(两分钟技就看懂)...

    其实登录注册,并且登录一次保持登录的状态,是每个项目都需要实现的功能. 网上也有很多的方法,不过,不是通俗易懂,在这里说一下我自己的方法,非常简单实用 核心就是用localStorage存.取数据,这 ...

最新文章

  1. Visual C++ 2011-8-15
  2. 如何清除Git中的本地工作目录? [重复]
  3. 002_FastDFS单机部署
  4. Java泛型的类型擦除
  5. 编写一个函数实现从 1 到 n 共 n 个数的累加_leetcode306_go_累加数
  6. 五分钟了解数据库事务隔离
  7. 选购工业交换机时,工业交换机的IP等级多少比较合适?
  8. springboot jwt token前后端分离_基于Spring Boot+Spring Security+JWT+Vue前后端分离的开源项目...
  9. 【MyBatis框架】mapper配置文件-关于动态sql
  10. Web.xml详解(转)
  11. property内存管理策略
  12. 为什么实验是领英 DNA 的核心部分?
  13. Git常见相关知识与命令
  14. 面试题:Java对象不再使用时,为什么要赋值为null?
  15. BZOJ3680 吊打XXX
  16. python保存的快捷键_新手学Python需要知道的Pycharm常用快捷键总结及配置方法
  17. 关于 Google play 上架的缺失 64 位版本问题
  18. java + selenium 实现QQ快速安全登录xx网站
  19. bearer token_接口认证方式:Bearer Token
  20. 计算机考研四大名著,2017考研英语翻译每日一句:四大名著

热门文章

  1. L1-012. 计算指数-PAT团体程序设计天梯赛GPLT
  2. Sublime Text编写80×86汇编.asm文件的语法高亮插件
  3. android 8.1.0怎么截屏,vivo Z1i怎么截屏?4种vivo Z1i截图方法
  4. python django 优势_为什么选择Django?
  5. vim nerdtree 标签_学业支持 | OS课程——给你的vim换套新衣服
  6. [性能优化]UITableView性能优化的一点感悟及计算UILabel高度的新方法 1
  7. 直播间搭建项目——延续直播发展趋势
  8. Mysql 的utf8和utf8mb4
  9. Huber损失最小化学习法
  10. 问题-Delphi 中使用TStringList后,报out of memory 的解决方法