基于事件的异步模式——BackgroundWorker
实现异步处理的方法很多,经常用的有基于委托的方式,今天记录的是基于事件的异步模式。利用BackgroundWorker组件可以很轻松的实现异步处理,并且该组件还支持事件的取消、进度报告等功能。本文以计算两个数X、Y的和为例。
通过反编译可以看到,这个组件内部也是通过异步委托实现的,报告进度、取消事件等运用了事件技术实现,而事件的本质其实就是委托。
- 程序界面如下图,其中三个文本框分别为两个加数和处理结果,两个按钮为计算和取消,按钮下方为进度条。
- 引入BackgroundWorker组件,为DoWork、ProgressChanged、RunWorkerCompleted三个事件指定方法。
将WorkerReportsProgress属性设为true,以支持报告进度。
将WorkerSupportsCancellation属性设置为true,以支持取消。public MainForm()
{
InitializeComponent();
this.backgroundWorker1.DoWork += this.BackgroundWorker1DoWork;
this.backgroundWorker1.ProgressChanged += this.BackgroundWorker1ProgressChanged;
this.backgroundWorker1.RunWorkerCompleted += this.BackgroundWorker1RunWorkerCompleted;
this.backgroundWorker1.WorkerReportsProgress = true;
this.backgroundWorker1.WorkerSupportsCancellation = true;
this.buttonCancel.Enabled = false;
} - 当点击计算按钮时,调用BackgroundWorker的RunWorkerAsync方法实现异步处理,该方法支持一个object类型的参数。这里用元组Tuple<int,int>传递X、Y的值。调用RunWorkerAsync方法,将触发DoWork事件。
this.backgroundWorker1.RunWorkerAsync(new Tuple<int,int>(Convert.ToInt32(this.textBoxX.Text), Convert.ToInt32(this.textBoxY.Text)));
通过反编译看到以下实现代码:
public void RunWorkerAsync(object argument)
{
if (this.isRunning)
{
throw new InvalidOperationException(SR.GetString("BackgroundWorker_WorkerAlreadyRunning"));
}
this.isRunning = true;
this.cancellationPending = false;
this.asyncOperation = AsyncOperationManager.CreateOperation(null);
this.threadStart.BeginInvoke(argument, null, null);
}可以看到其内部也是通过异步委托实现的,其中threadStart的定义:private delegate void WorkerThreadStartDelegate(object argument);就是一个委托类型。
- 当点击取消按钮时,调用BackgroundWorker的CancelAsync方法,这将改变BackgroundWorker的CancellationPending属性的值为ture。当执行OnWork方法时可以根据CancellationPending属性,判断用户是否要取消事件。
this.backgroundWorker1.CancelAsync();
再看看其内部:
public void CancelAsync()
{
if (!this.WorkerSupportsCancellation)
{
throw new InvalidOperationException(SR.GetString("BackgroundWorker_WorkerDoesntSupportCancellation"));
}
this.cancellationPending = true;
}如上面所说,改变只读属性CancellationPending的值为ture。
- 这里在DoWork方法中
根据CancellationPending属性,判断是否取消。若取消,应在方法结束之前将DoWorkEventArgs的Cancel属性设置为ture。这将用于RunWorkerCompleted事件中判断事件取消还是正常完成。
通过ReportProgress方法报告进度, 调用该方法时传递了一个int类型的参数,该方法将触发ProgressChanged事件。void BackgroundWorker1DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
for (int i = ; i < ; i++) {
System.Threading.Thread.Sleep();//模拟耗时操作
if (backgroundWorker1.CancellationPending) {
e.Cancel = true
return;
} else {
backgroundWorker1.ReportProgress(i * );
}
}
var t = e.Argument as Tuple<int,int>;
e.Result = t.Item1 + t.Item2;
}再看报告进度是怎么基于事件实现的:
1.我们调用的ReportProgress方法
public void ReportProgress(int percentProgress)
{
this.ReportProgress(percentProgress, null);
}
2.ReportProgress另一个重载,可以带一个自定义参数,方便传递其他内容,例如一般安装程序中的“正在复制文件”,“正在注册相关组件”提示信息等。
public void ReportProgress(int percentProgress, object userState)
{
if (!this.WorkerReportsProgress)
{
throw new InvalidOperationException(SR.GetString("BackgroundWorker_WorkerDoesntReportProgress"));
}
ProgressChangedEventArgs progressChangedEventArgs = new ProgressChangedEventArgs(percentProgress, userState);
if (this.asyncOperation != null)
{
this.asyncOperation.Post(this.progressReporter, progressChangedEventArgs);
return;
}
this.progressReporter(progressChangedEventArgs);
}
3.触发事件
private void ProgressReporter(object arg)
{
this.OnProgressChanged((ProgressChangedEventArgs)arg);
} - ProgressChanged事件,会将控制权交给UI线程。在其实现方法中根据ProgressChangedEventArgs的ProgressPercentage属性获取进度值。
void BackgroundWorker1ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
} - OnWork退出后,将调用RunWorkerCompleted事件。
根据RunWorkerCompletedEventArgs的Cancelled属性判断是否正常完成。
根据RunWorkerCompletedEventArgs的Result属性获取处理结果。void BackgroundWorker1RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
this.textBoxResult.Text = e.Cancelled ? "Canceled" : e.Result.ToString();
this.buttonCancel.Enabled = false;
this.buttonCalculate.Enabled = true;
this.progressBar1.Value = ;
} - 完整代码
/*
* 由SharpDevelop创建。
* 用户: David Huang
* 日期: 2015/9/8
* 时间: 14:54
*/
using System;
using System.Windows.Forms; namespace BackgroundWorkerTest
{
/// <summary>
/// Description of MainForm.
/// </summary>
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
this.backgroundWorker1.DoWork += this.BackgroundWorker1DoWork;
this.backgroundWorker1.ProgressChanged += this.BackgroundWorker1ProgressChanged;
this.backgroundWorker1.RunWorkerCompleted += this.BackgroundWorker1RunWorkerCompleted;
this.backgroundWorker1.WorkerReportsProgress = true;
this.backgroundWorker1.WorkerSupportsCancellation = true;
this.buttonCancel.Enabled = false;
}
void ButtonCalculateClick(object sender, EventArgs e)
{
this.buttonCalculate.Enabled = false;
this.textBoxResult.Text = string.Empty;
this.buttonCancel.Enabled = true;
this.backgroundWorker1.RunWorkerAsync(new Tuple<int,int>(Convert.ToInt32(this.textBoxX.Text), Convert.ToInt32(this.textBoxY.Text)));
}
void ButtonCancelClick(object sender, EventArgs e)
{
this.backgroundWorker1.CancelAsync();
}
void BackgroundWorker1DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
for (int i = ; i < ; i++) {
System.Threading.Thread.Sleep();
if (backgroundWorker1.CancellationPending) {
e.Cancel = true;
return;
} else {
backgroundWorker1.ReportProgress(i * );
}
}
var t = e.Argument as Tuple<int,int>;
e.Result = t.Item1 + t.Item2;
}
void BackgroundWorker1ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}
void BackgroundWorker1RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
this.textBoxResult.Text = e.Cancelled ? "Canceled" : e.Result.ToString();
this.buttonCancel.Enabled = false;
this.buttonCalculate.Enabled = true;
this.progressBar1.Value = ;
}
}
}代码下载:点我
基于事件的异步模式——BackgroundWorker的更多相关文章
- 基于事件的异步模式(EAP)
什么是EAP异步编程模式 EAP基于事件的异步模式是.net 2.0提出来的,实现了基于事件的异步模式的类将具有一个或者多个以Async为后缀的方法和对应的Completed事件,并且这些类都支持异步 ...
- Event-based Asynchronous Pattern Overview基于事件的异步模式概览
https://msdn.microsoft.com/zh-cn/library/wewwczdw(v=vs.110).aspx Applications that perform many task ...
- C#中的异步调用及异步设计模式(三)——基于事件的异步模式
四.基于事件的异步模式(设计层面) 基于事件的C#异步编程模式是比IAsyncResult模式更高级的一种异步编程模式,也被用在更多的场合.该异步模式具有以下优点: · ...
- 【温故知新】C#基于事件的异步模式(EAP)
在开发winform和调用asp.net的web service引用的时候,会出现许多命名为 MethodNameAsync 的方法. 例如: winform的按钮点击 this.button1.Cl ...
- 二、基于事件的异步编程模式(EAP)
一.引言 在上一个专题中为大家介绍了.NET 1.0中提出来的异步编程模式--APM,虽然APM为我们实现异步编程提供了一定的支持,同时它也存在着一些明显的问题--不支持对异步操作的取消和没有提供对进 ...
- 异步编程(二)基于事件的异步编程模式 (EAP)
一.引言 在上一个专题中为大家介绍了.NET 1.0中提出来的异步编程模式——APM,虽然APM为我们实现异步编程提供了一定的支持,同时它也存在着一些明显的问题——不支持对异步操作的取消和没有提供对进 ...
- .NET - 基于事件的异步模型
注:这是大概四年前写的文章了.而且我离开.net领域也有四年多了.本来不想再发表,但是这实际上是Active Object模式在.net中的一种重要实现方法,因此我把它掏出来发布一下.如果该模型有新的 ...
- C#基于任务的异步模式
using System; using System.Threading; using System.Threading.Tasks; using static System.Console; //异 ...
- .NET 基于任务的异步模式(Task-based Asynchronous Pattern,TAP) async await
本文内容 概述 编写异步方法 异步程序中的控制流 API 异步方法 线程 异步和等待 返回类型和参数 参考资料 下载 Demo 下载 Demo TPL 与 APM 和 EAP 结合(APM 和 EAP ...
随机推荐
- hdu 5067 Harry And Dig Machine
http://acm.hdu.edu.cn/showproblem.php?pid=5067 思路:问题可以转化成:从某一点出发,遍历网格上的一些点,每个点至少访问一次需要的最小时间是多少.这就是经典 ...
- SQLserver 数据库
1.数据库: 结构化查询语言(Structured Query Language)简称SQL: 数据库管理系统(Database Management System)简称DBMS: 数据库管理员(Da ...
- bzoj1257
这道题初看确实没什么思路,感觉之前的数论知识都用不上,只好自己找规律首先当n>=k 这部分是很容易直接算出的下面我们先来尝试这穷举i,不难发现当穷举i时,总存在一段连续的除数,k div i=p ...
- Android TextView setText内嵌html标签
由于得到的数据是保存在数据库里面的,不好对数据的某一部分进行操作.解决办法在数据库里面存数据的时候加上html的标签对, 如data = <中华人名共和国道路交通安全实施条例>第<u ...
- 移动存储卡仍然用FAT32文件系统的真相
微软在2001年就为自家的XP系统的本地磁盘默认使用了NTFS文件系统,但是12年之后,市面上的USB可移动设备和SD卡等外置存储器仍然在用着FAT32文件格式,这是什么理由让硬件厂商选择过时的文件系 ...
- iOS屏蔽高频点击技巧
例如高频率点击一个按钮或者TableViewCell,会造成功能多次重复执行,在异步网络请求时候或者多线程时候,造成的问题尤其明显. 解决方法: 声明一个属性self.actionWorking ,标 ...
- HDU2037 今年暑假不AC 贪心算法
贪心算法 : 贪心算法就是只考虑眼前最优解而忽略整体的算法, 它所做出的仅是在某种意义上的局部最优解, 然后通过迭代的方法相继求出整体最优解. 但是不是所有问题都可以得到整体最优解, 所以选择贪心策略 ...
- bzoj4448 SCOI2015 情报传递 message
传送门bzoj4448 题解 离线之后构建树上主席树,每个点的线段树维护到根路径的信息,不用链剖(我的链剖只是拿来求\(\mathrm{lca}\)的),时空复杂度\(O(n\log{n})\). c ...
- Cadence 信号完整性(一)-- 仿真步骤3
(2)单击“Identify DC Nets”,弹出“Identify DC Nets”窗口,如图2-6 所示: 图 2-6 Identify DC Nets 窗口 (3)在“Net”列表中选择网络如 ...
- winform 跨窗体给控件传值 分类: WinForm 2014-08-02 16:33 195人阅读 评论(0) 收藏
两个窗体 FormA,FormB; FormA窗体中有一文本框控件:TextBox; FormB窗体中有一变量:txtJSJ 目的:把变量赋值给文本框 实现: 设置TextBox属性: Modifie ...