基于事件的异步模式概述

MSDN

那些同时执行多项任务、但仍能响应用户交互的应用程序通常需要实施一种使用多线程的设计方案。System.Threading 命名空间提供了创建高性能多线程应用程序所必需的所有工具,但要想有效地使用这些工具,需要有丰富的使用多线程软件工程的经验。对于相对简单的多线程应用程序,BackgroundWorker 组件提供了一个简单的解决方案。对于更复杂的异步应用程序,请考虑实现一个符合基于事件的异步模式的类。

基于事件的异步模式具有多线程应用程序的优点,同时隐匿了多线程设计中固有的许多复杂问题。使用支持此模式的类,您将能够:

  • “在后台”执行耗时任务(例如下载和数据库操作),但不会中断您的应用程序。

  • 同时执行多个操作,每个操作完成时都会接到通知。

  • 等待资源变得可用,但不会停止(“挂起”)您的应用程序。

  • 使用熟悉的事件和委托模型与挂起的异步操作通信。有关使用事件处理程序和委托的更多信息,请参见事件和委托。

支持基于事件的异步模式的类将有一个或多个名为 MethodNameAsync 的方法。这些方法可能会创建同步版本的镜像,这些同步版本会在当前线程上执行相同的操作。此类还可能有一个MethodNameCompleted 事件,而且它可能会有一个 MethodNameAsyncCancel(或只是 CancelAsync)方法。

PictureBox 是一个支持基于事件的异步模式的典型组件。您可以通过调用其 Load 方法来同步下载图像,但是如果图像很大,或者网络连接很慢,您的应用程序将停止(“挂起”),直到下载操作完成并且对 Load 的调用返回后才会继续执行。

如果您希望您的应用程序在加载图像时保持运行,您可以调用 LoadAsync 方法,处理 LoadCompleted 事件,这与您处理任何其他事件没有什么两样。调用 LoadAsync 方法时,您的应用程序将继续运行,而下载操作将在另一个线程上(“在后台”)继续。图像加载操作完成时,将会调用您的事件处理程序,您的事件处理程序可以检查 AsyncCompletedEventArgs 参数以确定下载是否已成功完成。

基于事件的异步模式要求异步操作可以取消,PictureBox 控件使用其 CancelAsync 方法来支持此要求。调用 CancelAsync 会提交一个停止挂起的下载的请求,任务取消时会引发 LoadCompleted事件。

警告

下载有可能恰在发出 CancelAsync 请求时完成,因此 Cancelled 可能没有反映取消请求。这叫做“争用条件”,是多线程编程中常见的一个问题。有关多线程编程中的问题的更多信息,请参见托管线程处理的最佳做法。

基于事件的异步模式的特征

基于事件的异步模式可以采用多种形式,具体取决于某个特定类支持的操作的复杂程度。最简单的类可能只有一个 MethodNameAsync 方法和一个对应的 MethodNameCompleted 事件。更复杂的类可能有若干个 MethodNameAsync 方法(每种方法都有一个对应的 MethodNameCompleted 事件),以及这些方法的同步版本。这些类分别支持各种异步方法的取消、进度报告和增量结果。

异步方法可能还支持多个挂起的调用(多个并行调用),允许您的代码在此方法完成其他挂起的操作之前调用此方法任意多次。若要正确处理此种情况,必须让您的应用程序能够跟踪各个操作的完成。

基于事件的异步模式示例

SoundPlayer 和 PictureBox 组件表示基于事件的异步模式的简单实现。WebClient 和 BackgroundWorker 组件表示基于事件的异步模式的更复杂的实现。

下面是一个符合此模式的类声明示例:

public class AsyncExample
{// Synchronous methods.public int Method1(string param);public void Method2(double param);// Asynchronous methods.public void Method1Async(string param);public void Method1Async(string param, object userState);public event Method1CompletedEventHandler Method1Completed;public void Method2Async(double param);public void Method2Async(double param, object userState);public event Method2CompletedEventHandler Method2Completed;public void CancelAsync(object userState);public bool IsBusy { get; }// Class implementation not shown.
}

这里虚构的 AsyncExample 类有两个方法,都支持同步和异步调用。同步重载的行为类似于方法调用,它们对调用线程执行操作;如果操作很耗时,则调用的返回可能会有明显的延迟。异步重载将在另一个线程上启动操作,然后立即返回,允许在调用线程继续执行的同时让操作“在后台”执行。

异步方法重载

异步操作可以有两个重载:单调用和多调用。您可以通过方法签名来区分这两种形式:多调用形式有一个额外的参数,即 userState。使用这种形式,您的代码可以多次调用 Method1Async(string param, object userState),而不必等待任何挂起的异步操作的完成。另一方面,如果您尝试在前一个调用尚未完成时调用 Method1Async(string param),该方法将引发InvalidOperationException。

多调用重载的 userState 参数可帮助您区分各个异步操作。您应分别为各个 Method1Async(string param, object userState) 调用提供一个唯一值(例如 GUID 或哈希代码);这样,当各个操作完成时,您的事件处理程序便可以确定哪个操作的实例引发了完成事件。

跟踪挂起的操作

如果您使用多调用重载,您的代码将需要跟踪挂起的任务的 userState 对象(任务 ID)。对于每个 Method1Async(string param, object userState) 调用,您通常应生成一个新的、唯一的userState 对象并将此对象添加到集合中。当对应于此 userState 对象的任务引发完成事件时,您的完成方法实现将检查 System.ComponentModel.AsyncCompletedEventArgs.UserState 并将此对象从集合中删除。在以这种方式使用时,userState 参数充当任务 ID 的角色。

注意

在为您对多调用重载的调用中的 userState 提供唯一值时,一定要小心。如果任务 ID 不唯一,将导致异步类引发 ArgumentException。

取消挂起的操作

我们必须能够在异步操作完成之前随时取消它们,这一点很重要。实现基于事件的异步模式的类将有一个 CancelAsync 方法(如果有多个异步方法)或 MethodNameAsyncCancel 方法(如果只有一个异步方法)。

允许多个调用的方法采用 userState 参数,此参数可用来跟踪各个任务的生存期。CancelAsync 采用 userState 参数,此参数允许您取消特定的挂起任务。

一次只支持一个挂起的操作的方法(如 Method1Async(string param))是不可取消的。

接收进度更新和增量结果

符合基于事件的异步模式的类可以为跟踪进度和增量结果提供事件。此事件通常叫做 ProgressChanged 或 MethodNameProgressChanged,它对应的事件处理程序会带有一个ProgressChangedEventArgs 参数。

ProgressChanged 事件的事件处理程序可以检查 System.ComponentModel.ProgressChangedEventArgs.ProgressPercentage 属性来确定异步任务完成的百分比。此属性的范围是 0 到 100,可用来更新 ProgressBar 的 Value 属性。如果有多个异步操作挂起,您可以使用 System.ComponentModel.ProgressChangedEventArgs.UserState 属性来分辨出哪个操作在报告进度。

一些类可能会在异步操作继续时报告增量结果。这些结果将保存的派生自 ProgressChangedEventArgs 的类中,并显示为此派生类中的属性。您可以在 ProgressChanged 事件的事件处理程序中访问这些结果,就像访问 ProgressPercentage 属性一样。如果有多个异步操作挂起,您可以使用 UserState 属性来分辨出哪个操作在报告增量结果。

请参见

任务

如何:使用支持基于事件的异步模式的组件
如何:在后台运行操作
如何:实现使用后台操作的窗体

参考

ProgressChangedEventArgs
BackgroundWorker
AsyncCompletedEventArgs

概念

实现基于事件的异步模式的最佳做法
确定何时实现基于事件的异步模式

其他资源

使用基于事件的异步模式进行多线程编程

基于事件的异步模式概述相关推荐

  1. F#与ASP.NET(1):基于事件的异步模式与异步Action

    提高ASP.NET应用程序伸缩性的有效手段之一便是使用异步请求.而在ASP.NET MVC 1中是不能直接支持异步Action的,因此我们需要使用一些简单的Hack方式来实现这一点.不过简单的Hack ...

  2. 基于事件的异步模式——BackgroundWorker

    转自strangeman原文 基于事件的异步模式--BackgroundWorker 实现异步处理的方法很多,经常用的有基于委托的方式,今天记录的是基于事件的异步模式.利用BackgroundWork ...

  3. C# 基于事件的异步模式

    点击蓝字 关注我们 开工大吉 EventBasedAsyncPattern 方法使用了基于事件的异步模式.这个模式定义了一个带有 "Async" 后缀的方法.示例代码再次使用了We ...

  4. APM之基于事件的异步模式(EAP)-2

    EAP是针对Windows窗体开发提供的方便使用的异步模式,可以在IDE中可视化的设计和使用 // The System.Net.WebClient class supports the Event- ...

  5. 【转】1.7异步编程:基于事件的异步编程模式(EAP)

    传送门:异步编程系列目录-- 上一篇,我给大家介绍了".NET1.0 IAsyncResult异步编程模型(APM)",通过Begin*** 开启操作并返回IAsyncResult ...

  6. 基于任务的异步模式(TAP)

    Task .net 4.0为我们带来了Task的异步,我们有以下三种方法创建Task. 1,Task.Factory.StartNew,比较常用. 2,Task.Run,是.net 4.5中增加的. ...

  7. 与其他.Net异步模式和类型进行互操作

    返回该系列目录<基于Task的异步模式--全面介绍> Tasks和异步编程模型APM(Tasks and the Asynchronous Programming Model) 从APM到 ...

  8. .NET三种异步模式(APM、EAP、TAP)

    APM模式: .net 1.0时期就提出的一种异步模式,并且基于IAsyncResult接口实现BeginXXX和EndXXX类似的方法. .net中有很多类实现了该模式(比如HttpWebReque ...

  9. python异步框架twisted_twisted是python实现的基于事件驱动的异步网络通信构架。

    网:https://twistedmatrix.com/trac/ http://www.cnblogs.com/wy-wangyan/p/5252271.html What is Twisted? ...

最新文章

  1. miniui文件上传 linux,MINIUI grid学习笔记
  2. Linux文本处理三剑客之awk
  3. boost::format模块测试构造对象和基本解析
  4. SAP UI5 aggregation field type
  5. 在.net3.5中使用ListView控件和DataPager控件笔记
  6. Android Studio之package org.junit does not exist解决办法
  7. java实现表达式求值_如何编写一个高效的Java表达式求值程序
  8. 网页字体设置你了解吗?
  9. mongo快速翻页方法(转载)
  10. 郁金香商业辅助教程 2016 笔记 6~10
  11. jquery templates jQuery html模板
  12. 直观讲解--RPC调用和HTTP调用的区别
  13. 2020年考研准考证打印下载及注意事项
  14. Typora 下载方法(windows/ linux)
  15. 安装Navision Server5.0 注意事项
  16. Python 保留字和关键字的用法
  17. 哪里有周末java培训_北京哪里里Java周末学习班
  18. 芥川龙之介《烟草与魔鬼》
  19. 搜索引擎的网址收录链接
  20. 博主文章导航(分门别类,实时更新,永久置顶)

热门文章

  1. [日志]08/19/2007
  2. 从支付宝SDK的支付流程理解什么是公钥和私钥,什么是加密和数字签名
  3. 结对编程-四则运算生成
  4. CompletableFuture的runAsync
  5. Struts2——(3)ValueStack(值栈)
  6. Mac上安装homebrew(类似于Linux上的apt-get)
  7. 问题六十五:二叉查找树的一个应用实例——求解一元十次方程时单实根区间的划分
  8. java post上传进度,OkHttp实现文件上传进度
  9. 如何充分利用好大数据的价值
  10. 关于Runtime.getRuntime().exec()运行python代码失败的问题