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
<Window x:Class="BackgroundWorkerDemo20170324.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="342" Width="504">
<Grid>
<ProgressBar x:Name="pBar" Margin="50,69,43,209">
</ProgressBar>
<Button x:Name="btnStart" Content="start" Click="btnStart_Click" Margin="50,218,311,63"/>
<Button x:Name="btnCancel" Content="cancel" Click="btnCancel_Click" Margin="285,218,95,63"/>
</Grid>
</Window>
后台代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes; namespace BackgroundWorkerDemo20170324
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private BackgroundWorker worker;
public MainWindow()
{
InitializeComponent();
worker = new BackgroundWorker(); //新建BackgroundWorker
worker.WorkerReportsProgress = true; //允许报告进度
worker.WorkerSupportsCancellation = true; //允许取消线程
worker.DoWork += worker_DoWork; //设置主要工作逻辑
worker.ProgressChanged += worker_ProgressChanged; //进度变化的相关处理
worker.RunWorkerCompleted += worker_RunWorkerCompleted; //线程完成时的处理
} /// <summary>
/// 主要工作逻辑
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker tempWorker = sender as BackgroundWorker;
for (int i = ; i <= ; i++)
{
Thread.Sleep(); //避免太快,让线程暂停一会再报告进度
tempWorker.ReportProgress(i); //调用ReportProgress()方法报告进度,同时触发ProgressChanged事件
}
} /// <summary>
/// 处理进度变化,改变进度条的值
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pBar.Value = e.ProgressPercentage;
} /// <summary>
/// 线程完成后的处理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("线程工作完成");
} /// <summary>
/// 点击Start按钮启动线程
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnStart_Click(object sender, RoutedEventArgs e)
{
worker.RunWorkerAsync(); //调用该方法才会启动线程
} /// <summary>
/// 点击Cancel按钮取消线程,但先判断线程是否正在工作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
if (worker.IsBusy)
worker.CancelAsync();
else
MessageBox.Show("There is no thead running now.");
}
}
}
2.2.2. 能取消线程
在需要取消线程的位置判断CancellationPending属性,一般在循环体中(因为循环一般耗时居多),判断当CancellationPending==true时,
需要将DoWorkEventArgs的Cancel属性设置为true, 然后就可以在RunWorkerCompleted中判断RunWorkerCompletedEventArgs的Cancelled
属性来进行对应的处理(即,当用户取消线程时,也会触发RunWorkerCompleted事件)
修改后的代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes; namespace BackgroundWorkerDemo20170324
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private BackgroundWorker worker;
public MainWindow()
{
InitializeComponent();
worker = new BackgroundWorker(); //新建BackgroundWorker
worker.WorkerReportsProgress = true; //允许报告进度
worker.WorkerSupportsCancellation = true; //允许取消线程
worker.DoWork += worker_DoWork; //设置主要工作逻辑
worker.ProgressChanged += worker_ProgressChanged; //进度变化的相关处理
worker.RunWorkerCompleted += worker_RunWorkerCompleted; //线程完成时的处理
} /// <summary>
/// 主要工作逻辑
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker tempWorker = sender as BackgroundWorker;
for (int i = ; i <= ; i++)
{
if (tempWorker.CancellationPending) //当点击Cancel按钮时,CancellationPending被设置为true
{
e.Cancel = true; //此处设置Cancel=true后,就可以在RunWorkerCompleted中判断e.Cancelled是否为true
break;
}
Thread.Sleep(); //避免太快,让线程暂停一会再报告进度
tempWorker.ReportProgress(i); //调用ReportProgress()方法报告进度,同时触发ProgressChanged事件
}
} /// <summary>
/// 处理进度变化,改变进度条的值
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pBar.Value = e.ProgressPercentage;
} /// <summary>
/// 线程完成后的处理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled) //被取消时
MessageBox.Show("线程被取消了");
else //正常结束
MessageBox.Show("线程工作完成");
} /// <summary>
/// 点击Start按钮启动线程
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnStart_Click(object sender, RoutedEventArgs e)
{
worker.RunWorkerAsync(); //调用该方法才会启动线程
} /// <summary>
/// 点击Cancel按钮取消线程,但先判断线程是否正在工作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
if (worker.IsBusy)
worker.CancelAsync();
else
MessageBox.Show("There is no thead running now.");
}
}
}
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事件提供数据,详细代码如下:
#region 程序集 System.dll, v2.0.0.0
// C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll
#endregion using System; namespace System.ComponentModel
{
// 摘要:
// 为 System.ComponentModel.BackgroundWorker.DoWork 事件处理程序提供数据。
public class DoWorkEventArgs : CancelEventArgs
{
// 摘要:
// 初始化 System.ComponentModel.DoWorkEventArgs 类的新实例。
//
// 参数:
// argument:
// 指定异步操作的参数。
public DoWorkEventArgs(object argument); // 摘要:
// 获取表示异步操作参数的值。
//
// 返回结果:
// 表示异步操作参数的 System.Object。
public object Argument { get; }
//
// 摘要:
// 获取或设置表示异步操作结果的值。
//
// 返回结果:
// 表示异步操作结果的 System.Object。
public object Result { get; set; }
}
}
只有两个object类型的属性Argument(只读的)和Result,
因为调用RunWorkerAsync()方法,就会触发DoWork事件,细心的你会发现,RunWorkerAsync()方法有两个重载,
带有参数的RunWorkerAsync(object argument),这个形参argument就是传递给DoWorkEventArgs的Arument属性的
而Result属性是作为DoWork事件的结果传递给RunWorkerCompletedEventArgs的Result属性,
3.1.2. ProgressChangedEventArgs 为ProgressChanged事件提供数据,详细代码如下:
using System; namespace System.ComponentModel
{
// 摘要:
// 为 System.ComponentModel.BackgroundWorker.ProgressChanged 事件提供数据。
public class ProgressChangedEventArgs : EventArgs
{
// 摘要:
// 初始化 System.ComponentModel.ProgressChangedEventArgs 类的新实例。
//
// 参数:
// progressPercentage:
// 已完成的异步任务的百分比。
//
// userState:
// 唯一的用户状态。
public ProgressChangedEventArgs(int progressPercentage, object userState); // 摘要:
// 获取异步任务的进度百分比。
//
// 返回结果:
// 指示异步任务进度的百分比值。
public int ProgressPercentage { get; }
//
// 摘要:
// 获取唯一的用户状态。
//
// 返回结果:
// 指示用户状态的唯一 System.Object。
public object UserState { get; }
}
}
也只有两个属性,都是只读的,一个为int类型的ProgerssPercentage,表示任务进度百分百;另一个是object类型的UserState
这两个参数都是通过ReportProgress()方法传入,由于UserState属性时object类型的,所以当需要实现复制逻辑时,可以自定义一个类型
3.1.3. RunWorkerCompletedEventArgs 稍微特殊一点,虽然该类的直接定义中只有两个属性,
object类型的两个只读属性Result和UserState
namespace System.ComponentModel
{
public class RunWorkerCompletedEventArgs : AsyncCompletedEventArgs
{
public RunWorkerCompletedEventArgs(object result, Exception error, bool cancelled); public object Result { get; }
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public object UserState { get; }
}
}
但从父类继承过来的Cancelled和Error属性才是重点
namespace System.ComponentModel
{
public class AsyncCompletedEventArgs : EventArgs
{
public AsyncCompletedEventArgs(Exception error, bool cancelled, object userState); [SRDescription("Async_AsyncEventArgs_Cancelled")]
public bool Cancelled { get; }
[SRDescription("Async_AsyncEventArgs_Error")]
public Exception Error { get; }
[SRDescription("Async_AsyncEventArgs_UserState")]
public object UserState { get; } protected void RaiseExceptionIfNecessary();
}
}
所以,RunWorkerCompletedEventArgs参数主要关心三个属性,Error、Cancelled、Result (UserState一般很少用到)
注意:标准的RunWorkerCompleted处理都应该先处理Error和Cancelled情况,否则直接访问Result时会报错,
尽量以这种方式来整理自己的代码逻辑:
// This event handler deals with the results of the
// background operation.
private void backgroundWorker1_RunWorkerCompleted(
object sender, RunWorkerCompletedEventArgs e)
{
// First, handle the case where an exception was thrown.
if (e.Error != null)
{
MessageBox.Show(e.Error.Message);
}
else if (e.Cancelled)
{
// Next, handle the case where the user canceled
// the operation.
// Note that due to a race condition in
// the DoWork event handler, the Cancelled
// flag may not have been set, even though
// CancelAsync was called.
resultLabel.Text = "Canceled";
}
else
{
// Finally, handle the case where the operation
// succeeded.
resultLabel.Text = e.Result.ToString();
}
}
4.注意事项
4.1. DoWork事件中不能与UI控件进行交流
如果需要在线程处理过程中与UI控件进行交流,请在ProgressChanged和RunWorkerCompleted中进行,否则会出现以下错误
System.InvalidOperationException was unhandled by user code
Message=The calling thread cannot access this object because a different thread owns it.
Source=WindowsBase
StackTrace:
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显示如下:
代码:
using System; namespace System.ComponentModel
{
[DefaultEvent("DoWork")]
public class BackgroundWorker : Component
{
public BackgroundWorker(); [Browsable(false)]
public bool CancellationPending { get; } //用于判断是否取消了线程
[Browsable(false)]
public bool IsBusy { get; } //判断当前线程是否正在工作
[DefaultValue(false)]
public bool WorkerReportsProgress { get; set; } //是否报告进度
[DefaultValue(false)]
public bool WorkerSupportsCancellation { get; set; } //是否支持取消线程,如果要调用CancelAsyns()方法,必须设置为true public event DoWorkEventHandler DoWork; //线程核心代码,耗时的操作放在这里,调用RunWorkerAsync()时触发
public event ProgressChangedEventHandler ProgressChanged; //当进度改变后执行的代码,调用ReportProgress()时触发
public event RunWorkerCompletedEventHandler RunWorkerCompleted; //3种情况:当线程完成、手动取消线程时、线程发生异常时触发 public void CancelAsync(); //调用该方法会发出取消线程的消息,但并不会立即中止线程
protected virtual void OnDoWork(DoWorkEventArgs e);
protected virtual void OnProgressChanged(ProgressChangedEventArgs e);
protected virtual void OnRunWorkerCompleted(RunWorkerCompletedEventArgs e);
public void ReportProgress(int percentProgress); //调用该方法就触发ProgressChanged事件,相当于ReportProgress(int percentProgress, null)
public void ReportProgress(int percentProgress, object userState);
public void RunWorkerAsync(); //调用该方法启动线程,同时触发DoWork事件,相当于RunWorkerAsync(null)
public void RunWorkerAsync(object argument);
}
}
5.2. 相关博客推荐,非常优秀,值得查看:
http://m.blog.csdn.net/article/details?id=7291070
BackgroundWorker简单实用(简便的异步操作)的更多相关文章
- Linux下好用的简单实用命令
1.你是否为在输入了一大串命令之后发现第一个字符打错了而苦恼?只能删除重来嘛?或者一步步左移光标? NO,一个组合键轻松搞定 Ctrl+A -----到命令行首 Ctrl+E ------到命令行末 ...
- jQuery的几种简单实用效果
许久未分享博客,或许已生疏. 闲来无事, 分享几个jQuery简单实用的效果案例 不喜勿喷... 1.页面常用的返回顶部 <!DOCTYPE html> <html lang=&qu ...
- 经验分享:10个简单实用的 jQuery 代码片段
尽管各种 JavaScirpt 框架和库层出不穷,jQuery 仍然是 Web 前端开发中最常用的工具库.今天,向大家分享我觉得在网站开发中10个简单实用的 jQuery 代码片段. 您可能感兴趣的相 ...
- 简单实用的PHP防注入类实例
这篇文章主要介绍了简单实用的PHP防注入类实例,以两个简单的防注入类为例介绍了PHP防注入的原理与技巧,对网站安全建设来说非常具有实用价值,需要的朋友可以参考下 本文实例讲述了简单实用的PHP防注 ...
- php简单实用的操作文件工具类(创建、移动、复制、删除)
php简单实用好用的文件及文件夹复制函数和工具类(创建.移动.复制.删除) function recurse_copy($src,$dst) { // 原目录,复制到的目录 $dir = opend ...
- 基于Bootstrap简单实用的tags标签插件
http://www.htmleaf.com/jQuery/ jQuery之家 自由分享jQuery.html5和css3的插件库 基于Bootstrap简单实用的tags标签插件
- C#_简单实用的翻页
简单实用的生成翻页HTML辅助类 C# using System.Text; namespace ClassLibrary { /// <summary> /// /// </sum ...
- 简单实用的Windows命令(一)
前几天新买了一台笔记本电脑,使用了一下几个简单的查看电脑配置的命令,觉得非常的不错,在此记录一下 一:运行命令的方式有两种 1:使用快捷键WIN+R,然后在弹出的“运行”对话框中输入对应的命令 2:在 ...
- 简单实用的Windows命令(二)
昨天简单的记录了几个非常简单实用的Windows命令,不过我又想起来还有两个我在实际的工作中也是经常用到的命令——PING和IPCONFIG,不过我在工作中的使用都是非常简单的,用PING命令检测对应 ...
随机推荐
- SSM_CRUD新手练习(4)修改生成的mapper.xml映射文件
我们为什么要修改呢,这是因为我们查询的时候,我们有时候需要连表查询,例如我们需要查询出员工表的信息(emp_id,emp_name...)与此同时,我们还想查询出该员工所在的部门(dept_name) ...
- noip第19课作业
1. 谁考了第K名 在一次考试中,每个学生的成绩都不相同,现知道了每个学生的学号和成绩,求考第k名学生的学号和成绩. [输入格式] 第一行有两个整数,分别是学生的人数n(1<=n<=100 ...
- 谈谈HashMap线程不安全的体现
原文出处: Hosee HashMap的原理以及如何实现,之前在JDK7与JDK8中HashMap的实现中已经说明了. 那么,为什么说HashMap是线程不安全的呢?它在多线程环境下,会发生什么情况呢 ...
- utf8.php
<?php /** * */ class Utf8 { function __construct() { global $CFG; if( preg_match('/./u', '') === ...
- 第80讲:List的泛型分析以及::类和Nil对象
今天我们学习一下scala中的列表,List. 通过源码,我们可以发现,List类型是协变的,所以我们可以把Int类型的List赋值给Any型的List. 我们可以看到,List定义下有3个比较重要的 ...
- Swift可向上滑移出界面的欢迎页简单封装
使用: -(WelcomView*)welcomeView{ if (!_welcomeView) { _welcomeView = [[NSBundle mainBundle] loadNibNam ...
- cxGrid单元格获得输入焦点
cxGrid单元格获得输入焦点 cxGrid单元格获得输入焦点 cxGrid1.SetFocus;cxGrid1DBTableView1.Controller.EditingController. ...
- python安装mysql-python依赖包
# 背景 新公司,对换工作了!接口自动化使用的是python的behave框架,因此需要折腾python了,而公司配的笔记本是windows的,因此要在windows下折腾python了 # 步骤 项 ...
- Linux-切换启动方式
Linx 默认的启动方式可以用图形界面也可以用命令行状态,命令行状态的启动相对来说运行速度更快,而且资源的消耗也更小,这个可以在Linux启动的过程中修改,也可直接修改配置文件来进行设置默认的启动方式 ...
- MVC5控制器传值的三种方式(ViewData,ViewBag,TempData),刚刚学习MVC5的新手,希望各位大神多多指教
mvc传值的三种方式:1.ViewData 在使用过程中需要类型转换 例子: ViewData["MyTitle"]="ViewData传值"; 引用: @Vi ...