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命令检测对应 ...
随机推荐
- linux 修改密码
1.开机 2.按下e键 直接进入编辑页面 3.找到ro 删除ro那一段 输入 rw init=/sysroot/bin/sh 4.进入单用户模式 5.改变程序执行时所参考的根目录位置 chroot / ...
- 【python3+request】python3+requests接口自动化测试框架实例详解教程
转自:https://my.oschina.net/u/3041656/blog/820023 [python3+request]python3+requests接口自动化测试框架实例详解教程 前段时 ...
- NoSQL世界的几个重要理论
和所有事物一样,NoSQL的兴起也是由许多理论支撑作为前提的,正是由下面一些理论的支撑,NoSQL的方向才能如此明朗. 1.CAP理论 CAP理论无疑是导致技术趋势由关系数据库系统向NoSQL系统转变 ...
- js-倒计时应用
<!DOCTYPE html><html> <head> <meta charset="UTF-8"> ...
- day06_雷神_面向对象初识
day_06 递归函数 自己用自己.一般递归100多次,都没有解决的问题,放弃递归. count = 0 def func1(): global count count += 1 print(coun ...
- 7.代理handler
简单的自定义opener() import urllib.request #构建一个HTTPHandler处理器对象,支持处理HTTP请求 http_handler=urllib.request.HT ...
- 【转】利用线程更新ListView (2014-09-28 08:25:20)
http://blog.sina.com.cn/s/blog_44fa172f0102v2x0.html procedure TForm5.Button3Click(Sender: TObject); ...
- 【转】jQuery.validate 用法
名称 返回类型 描述 validate(options) 返回:Validator ...
- 利用C#迭代器的一个杨辉三角示例
身边有个朋友在跟着廖雪峰的教程学习python,途中遇到了"在Python中使用迭代器打印杨辉三角"的问题,我在帮忙解决的同时顺手写了个简单的C#版本以供补充. internal ...
- datetime & time
python有两个和时间相关的模块,datetime和time datetime datetime模块下有四个类 date 日期相关的 time 时间相关的 datetime ...