实现异步处理的方法很多,经常用的有基于委托的方式,今天记录的是基于事件的异步模式。利用BackgroundWorker组件可以很轻松的实现异步处理,并且该组件还支持事件的取消、进度报告等功能。本文以计算两个数X、Y的和为例。

    通过反编译可以看到,这个组件内部也是通过异步委托实现的,报告进度、取消事件等运用了事件技术实现,而事件的本质其实就是委托。

  1. 程序界面如下图,其中三个文本框分别为两个加数和处理结果,两个按钮为计算和取消,按钮下方为进度条。
  2. 引入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;
    }
  3. 当点击计算按钮时,调用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);就是一个委托类型。

  4. 当点击取消按钮时,调用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。

  5. 这里在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);
    }
  6. ProgressChanged事件,会将控制权交给UI线程。在其实现方法中根据ProgressChangedEventArgs的ProgressPercentage属性获取进度值。
    void BackgroundWorker1ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
    {
    this.progressBar1.Value = e.ProgressPercentage;
    }
  7. 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 = ;
    }
  8. 完整代码
     /*
    * 由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的更多相关文章

  1. 基于事件的异步模式(EAP)

    什么是EAP异步编程模式 EAP基于事件的异步模式是.net 2.0提出来的,实现了基于事件的异步模式的类将具有一个或者多个以Async为后缀的方法和对应的Completed事件,并且这些类都支持异步 ...

  2. Event-based Asynchronous Pattern Overview基于事件的异步模式概览

    https://msdn.microsoft.com/zh-cn/library/wewwczdw(v=vs.110).aspx Applications that perform many task ...

  3. C#中的异步调用及异步设计模式(三)——基于事件的异步模式

    四.基于事件的异步模式(设计层面) 基于事件的C#异步编程模式是比IAsyncResult模式更高级的一种异步编程模式,也被用在更多的场合.该异步模式具有以下优点: ·                 ...

  4. 【温故知新】C#基于事件的异步模式(EAP)

    在开发winform和调用asp.net的web service引用的时候,会出现许多命名为 MethodNameAsync 的方法. 例如: winform的按钮点击 this.button1.Cl ...

  5. 二、基于事件的异步编程模式(EAP)

    一.引言 在上一个专题中为大家介绍了.NET 1.0中提出来的异步编程模式--APM,虽然APM为我们实现异步编程提供了一定的支持,同时它也存在着一些明显的问题--不支持对异步操作的取消和没有提供对进 ...

  6. 异步编程(二)基于事件的异步编程模式 (EAP)

    一.引言 在上一个专题中为大家介绍了.NET 1.0中提出来的异步编程模式——APM,虽然APM为我们实现异步编程提供了一定的支持,同时它也存在着一些明显的问题——不支持对异步操作的取消和没有提供对进 ...

  7. .NET - 基于事件的异步模型

    注:这是大概四年前写的文章了.而且我离开.net领域也有四年多了.本来不想再发表,但是这实际上是Active Object模式在.net中的一种重要实现方法,因此我把它掏出来发布一下.如果该模型有新的 ...

  8. C#基于任务的异步模式

    using System; using System.Threading; using System.Threading.Tasks; using static System.Console; //异 ...

  9. .NET 基于任务的异步模式(Task-based Asynchronous Pattern,TAP) async await

    本文内容 概述 编写异步方法 异步程序中的控制流 API 异步方法 线程 异步和等待 返回类型和参数 参考资料 下载 Demo 下载 Demo TPL 与 APM 和 EAP 结合(APM 和 EAP ...

随机推荐

  1. 防止 DDoS 攻击的五个「大招」!

    提到 DDoS 攻击,很多人不会陌生.上周,美国当地时间 12 月 29 日,专用虚拟服务器提供商 Linode 遭到 DDoS 攻击,直接影响其 Web 服务器的访问,其中 API 调用和管理功能受 ...

  2. Qt4.8 移植(超详细Configure的参数)

    Qt4.8.6 configure 参数 不只是适用于Qt4.8.6,原则上适用于Qt4所有版本 Usage: configure [-h] [-prefix <dir>] [-prefi ...

  3. Chrome测试网站加载时间与流量消耗

    加载10M内容,耗时近10秒钟 大概页面图片下载时,同时进行了,有的大图,下载要几秒钟

  4. [LeetCode#274]H-Index

    Problem: Given an array of citations (each citation is a non-negative integer) of a researcher, writ ...

  5. 携手 Google 和 Docker 为 Microsoft Azure 带来全新的开源容器技术

     发布于 2014-08-08 作者 陈 忠岳 今天对容器管理来说是个好日子.因为今天微软开放技术不止发布一项,而是两项开源计划来帮助云开发者将容器管理又向前推进一步.我们已开始与 Google ...

  6. (转载)不能启动虚拟机 Unable to open kernel device "\\.\Global\vmx86

    (转载)http://blog.csdn.net/shenghuiping2001/article/details/7083153 今天系统加了内存条,设置变了一下: 就启动不起虚拟机了,报错: Un ...

  7. (转载)Mysql查找如何判断字段是否包含某个字符串

    (转载)http://www.th7.cn/db/mysql/201306/31159.shtml 有这样一个需求,在Mysql数据库字符串字段(权限)中,用户有多个不同的邮箱,分别被‘,’分开,现在 ...

  8. 元素重叠及position定位的z-index顺序

    元素位置重叠的背景常识 (x)html文档中的元素默认处于普通流(normal flow)中,也就是说其顺序由元素在文档中的先后位置决定,此时一般不会产生重叠(但指定负边距可能产生重叠).当我们用cs ...

  9. A*寻路算法的探寻与改良(二)

    A*寻路算法的探寻与改良(二) by:田宇轩                                                     第二部分:这部分内容主要是使用C语言编程实现A*, ...

  10. hdu 4284 状态压缩

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4284 #include<cstdio> #include<cstring> # ...