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命令检测对应 ...
随机推荐
- mach_absolute_time 使用
今天看荣哥时间常用函数封装里有个不常见的函数 ,mach_absolute_time() ,经查询后感觉是个不错的函数,网上关于这个函数搜索以后简单整理来一下. 什么事Mach? 时间例程依赖于所需要 ...
- html5 定位
需要实现的功能:移动端的网页,能定位到中文地址. 百度地图能实现这样的功能. 之前精度差得原因是,我用自己的mac做服务器,用手机来浏览定位,这样只能定位到mac 的地址,mac上浏览器的地址就没准了 ...
- SRM475
250pt: 题意:有最长N=17的一条格子,每个格子是W.B和R三种颜色之一,当某个格子上有兔子时,下一个回合该兔子按照以下的规则移动: 如果兔子在第一个格子,则向右移动一格: 否则如果兔子在倒数两 ...
- shell 命令 查看本机ip
ifconfig 结果有很多,查看env0的inet,就是本机的ip地址
- npm 及安装
一.npm nodejs使开发者摆脱了浏览器的束缚,一系列基于nodejs的应用和工具不断出现,无论是在node应用的开发,还是使用中,包管理都扮演着一个很重要的作用.NPM(node package ...
- 1.虚拟机中安装ubuntu
1.VMware安装很简单,全部默认安装即可. 2.安装完VMware之后,打开VMware,点击创建虚拟机 典型安装易出问题,所以这里选择自定义安装 安装过程选项配置如下 处理器数,核数,内存都可以 ...
- 在推送提交之后阻止Azure DevOps (TFS)持续集成
在Azure DevOps服务器上配置生成定义时,可以配置连续集成(CI)生成.每次签入或提交到源代码库时都会自动运行一个CI构建.这种机制允许开发人员启动一个自动化的过程,例如编译和部署构建.这是一 ...
- spark-mllib 密集向量和稀疏向量
spark-mllib 密集向量和稀疏向量 MLlib支持局部向量和矩阵存储在单台服务器,也支持存储于一个或者多个rdd的分布式矩阵 . 局部向量和局部矩阵是用作公共接口的最简单的数据模型. 基本的线 ...
- 多个文本框录入,使用回车键替找Tab键
为了快速把form的所有文框输入完毕,我们不必使用鼠标去focus文本框. 在html页中放几个文本框: <div class="DivInput"> <div& ...
- Android开发教程 - 使用Data Binding(七)使用BindingAdapter简化图片加载
本系列目录 使用Data Binding(一)介绍 使用Data Binding(二)集成与配置 使用Data Binding(三)在Activity中的使用 使用Data Binding(四)在Fr ...