BackgroundWorker简单实用(简便的异步操作)
微软提供了一个快捷使用多线程的帮助类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参数主要关心三个属性,Error、Cancelled、Result (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简单实用(简便的异步操作)相关推荐
- 分享几个用 Python 给图片添加水印的方法,简单实用
作者 |俊欣 来源 |关于数据分析与可视化 今天来分享几种可以给图片添加水印的方法,都是十分的简单实用,大家在看了之后也可以私底下去自己试试,有些方法需要的代码量就比较少,有些方法需要的代码量就稍微多 ...
- 【Python】分享几个用Python给图片添加水印的方法,简单实用
今天来分享几种可以给图片添加水印的方法,都是十分的简单实用,大家在看了之后也可以私底下去自己试试,有些方法需要的代码量就比较少,有些方法需要的代码量就稍微多一些,那我们开始吧 opencv模块 首先我 ...
- 太妙了!几个用Python给图片添加水印的方法,简单实用!
今天来分享几种可以给图片添加水印的方法,都是十分的简单实用,大家在看了之后也可以私底下去自己试试,有些方法需要的代码量就比较少,有些方法需要的代码量就稍微多一些,那我们开始吧 opencv模块 首先我 ...
- 用Python给图片添加水印的3种方法,简单实用
来源:关于数据分析与可视化 今天来分享几种可以给图片添加水印的方法,都是十分的简单实用,大家在看了之后也可以私底下去自己试试,有些方法需要的代码量就比较少,有些方法需要的代码量就稍微多一些,那我们开始 ...
- 一个简单实用的,基于EF的三层架构
到底什么样的框架才是好框架呢?或许不同人有不同的看法.我个人觉一个好的框架,最重要的要是简单实用,能快速适开发,可维护性高(不会出现复制黏贴的代码),并能快速响应各种业务场景的变化的框架,同时性能不会 ...
- html 可调节进度条控件,jQuery简单实用的轻量级进度条插件
jQMeter是一款简单实用的轻量级进度条jQuery插件,它可以显示为水平或垂直进度条,进度条加载时带有动画特效,你只需要简单的传入一些参数到jQMeter对象的构造函数中就可以完成你想要的进度条效 ...
- matlab简单程序实例_visual basic VB.NET实例系列教程第一节(简单实用抽奖程序)...
近期疫情原因,工作比较不忙,所以打算出一套零基础,VB.NET实例系列入门教程,实用又好玩,带大家进入VB的编程世界里,希望这套图文教程能帮到有需要的人! 第一节(简单实用抽奖程序) 内容准备:编译环 ...
- 用aspnetpager实现datalist分页(绝对的简单实用)
微软的Datalist在做电子相册时候必不可少,但是不支持分页功能,都出到2008了还是没有分页功能,幸好网上有个专业的分页空间aspnetpager,帮我们解决了大问题,说实在话的,网上关于data ...
- 这两天老是有兄弟问到Vue的登陆和注册,登陆成功留在首页,没有登录回到登录页面,现在我用最简单实用的方法实现(两分钟技就看懂)...
其实登录注册,并且登录一次保持登录的状态,是每个项目都需要实现的功能. 网上也有很多的方法,不过,不是通俗易懂,在这里说一下我自己的方法,非常简单实用 核心就是用localStorage存.取数据,这 ...
最新文章
- Visual C++ 2011-8-15
- 如何清除Git中的本地工作目录? [重复]
- 002_FastDFS单机部署
- Java泛型的类型擦除
- 编写一个函数实现从 1 到 n 共 n 个数的累加_leetcode306_go_累加数
- 五分钟了解数据库事务隔离
- 选购工业交换机时,工业交换机的IP等级多少比较合适?
- springboot jwt token前后端分离_基于Spring Boot+Spring Security+JWT+Vue前后端分离的开源项目...
- 【MyBatis框架】mapper配置文件-关于动态sql
- Web.xml详解(转)
- property内存管理策略
- 为什么实验是领英 DNA 的核心部分?
- Git常见相关知识与命令
- 面试题:Java对象不再使用时,为什么要赋值为null?
- BZOJ3680 吊打XXX
- python保存的快捷键_新手学Python需要知道的Pycharm常用快捷键总结及配置方法
- 关于 Google play 上架的缺失 64 位版本问题
- java + selenium 实现QQ快速安全登录xx网站
- bearer token_接口认证方式:Bearer Token
- 计算机考研四大名著,2017考研英语翻译每日一句:四大名著
热门文章
- L1-012. 计算指数-PAT团体程序设计天梯赛GPLT
- Sublime Text编写80×86汇编.asm文件的语法高亮插件
- android 8.1.0怎么截屏,vivo Z1i怎么截屏?4种vivo Z1i截图方法
- python django 优势_为什么选择Django?
- vim nerdtree 标签_学业支持 | OS课程——给你的vim换套新衣服
- [性能优化]UITableView性能优化的一点感悟及计算UILabel高度的新方法 1
- 直播间搭建项目——延续直播发展趋势
- Mysql 的utf8和utf8mb4
- Huber损失最小化学习法
- 问题-Delphi 中使用TStringList后,报out of memory 的解决方法