四、基于事件的异步模式(设计层面)
基于事件的C#异步编程模式是比IAsyncResult模式更高级的一种异步编程模式,也被用在更多的场合。该异步模式具有以下优点:
· “在后台”执行耗时任务(例如下载和数据库操作),但不会中断您的应用程序。
· 同时执行多个操作,每个操作完成时都会接到通知(在通知中可以区分是完成了哪个操作)。
· 等待资源变得可用,但不会停止(“挂起”)您的应用程序。
· 使用熟悉的事件和委托模型与挂起的异步操作通信。
对于相对简单的应用程序可以直接用 .Net 2.0 新增的 BackgroundWorker 组件来很方便的实现,对于更复杂的异步应用程序则需要自己实现一个符合基于事件的C#异步编程模式的类。在实现基于事件的异步模式的设计前,需要了解基于 事件的异步模式的实现原理是什么。基于事件的异步模式需要以下三个类型的帮助。
AsyncOperation:提供了对异步操作的生存期进行跟踪的功能,包括操作进度通知和操作完成通知,并确保在正确的线程或上下文中调用客户端的事件处理程序。
public void Post(SendOrPostCallback d,Object arg);
public void PostOperationCompleted(SendOrPostCallback d,Object arg);
通过在异步辅助代码中调用Post方法把进度和中间结果报告给用户,如果是取消异步任务或提示异步任务已完成,则通过调 用PostOperationCompleted方法结束异步操作的跟踪生命期。在PostOperationCompleted方法调用 后,AsyncOperation对象变得不再可用,再次访问将引发异常。在此有个问题:在该异步模式中,通过AsyncOperation的Post函数来通知进度的时候,是如何使SendOrPostCallback委托在UI线程上执行的?针对该问题下文有具体分析。
AsyncOperationManager:为AsyncOperation对象的创建提供了便捷方式,通过CreateOperation方法可以创建多个AsyncOperation实例,实现对多个异步操作进行跟踪。
WindowsFormsSynchronizationContext:该类继承自SynchronizationContext类型,提供 Windows 窗体应用程序模型的同步上下文。该类型是基于事件异步模式通信的核心。之所以说该类型是基于事件异步模式的通信核心,是因为该类型解决了“保证SendOrPostCallback委托在UI线程上执行”的问题。它是如何解决的?请看AsyncOperation类型的Post方法的实现:
- /// <summary>
- /// AsyncOperation类型的Post方法的实现
- /// </summary>
- public void Post(SendOrPostCallback d, object arg)
- {
- this.VerifyNotCompleted();
- this.VerifyDelegateNotNull(d);
- this.syncContext.Post(d, arg);
- }
在AsyncOperation类型的Post方法中,直接调用了SynchronizationContext类型的Post方法,再看该Post方法的实现:
- /// <summary>
- /// WindowsFormsSynchronizationContext类型的Post方法的实现
- /// </summary>
- public override void Post(SendOrPostCallback d, object state)
- {
- if (this.controlToSendTo != null)
- {
- this.controlToSendTo.BeginInvoke(d, new object[] { state }); //此处保证了SendOrPostCallBack委托在UI线程上执行
- }
- }
有以上三个类型(AsyncOpertion,AsyncOperationManager和SynchronizationContext)作为基础,实现基于事件的异步模式的进度通知和完成通知就轻松多了。下面用一个基于事件的异步模型的例子来结束本文章。
- using System;
- using System.Collections.Generic;
- using System.Text;
- using System.ComponentModel;
- using System.Collections.Specialized;
- using System.Threading;
- namespace test
- {
- /// <summary>
- /// 任务1的进度通知代理
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- public delegate void Work1ProgressChangedEventHandler(object sender, Work1ProgressChangedEventArgs e);
- /// <summary>
- /// 任务1的进度通知参数
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- public delegate void Work1CompletedEventHandler(object sender, Work1CompletedEventArgs e);
- public class BasedEventAsyncWorker
- {
- private delegate void WorkerEventHandler(int maxNumber, AsyncOperation asyncOp);
- private HybridDictionary userStateToLifetime = new HybridDictionary();
- public BasedEventAsyncWorker()
- { }
- #region DoWork1的基于事件的异步调用
- public void DoWork1Async(object userState, int maxNumber)
- {
- AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(userState);
- //userStateToLifetime有可能会同时被多线程访问,在此需要lock进行同步处理
- lock (userStateToLifetime.SyncRoot)
- {
- if (userStateToLifetime.Contains(userState))
- {
- throw new ArgumentException(
- "userState parameter must be unique",
- "userState");
- }
- userStateToLifetime[userState] = asyncOp;
- }
- //异步开始任务1
- WorkerEventHandler workerDelegate = new WorkerEventHandler(DoWork1);
- workerDelegate.BeginInvoke(maxNumber, asyncOp, null, null);
- }
- private void DoWork1(int maxNumber, AsyncOperation asyncOp)
- {
- Exception e = null;
- //判断该userState的任务仍在处理中
- if (!TaskCanceled(asyncOp.UserSuppliedState))
- {
- try
- {
- int n = 0;
- int percentage = 0;
- while (n < maxNumber && !TaskCanceled(asyncOp.UserSuppliedState))
- {
- Thread.Sleep(100); //模拟耗时操作
- percentage = (int)((float)n / (float)maxNumber * 100);
- Work1ProgressChangedEventArgs progressChanageArgs =
- new Work1ProgressChangedEventArgs(maxNumber, percentage, asyncOp.UserSuppliedState);
- //任务1的进度通知
- asyncOp.Post(new SendOrPostCallback(Work1ReportProgressCB), progressChanageArgs);
- n++;
- }
- }
- catch (Exception ex)
- {
- e = ex;
- }
- }
- this.Work1Complete(e, TaskCanceled(asyncOp.UserSuppliedState), asyncOp);
- }
- private void Work1Complete(Exception exception, bool canceled, AsyncOperation asyncOp)
- {
- if (!canceled)
- {
- lock (userStateToLifetime.SyncRoot)
- {
- userStateToLifetime.Remove(asyncOp.UserSuppliedState);
- }
- }
- Work1CompletedEventArgs e = new Work1CompletedEventArgs(exception, canceled, asyncOp.UserSuppliedState);
- //通知指定的任务已经完成
- asyncOp.PostOperationCompleted(new SendOrPostCallback(Work1CompleteCB), e);
- //调用 PostOperationCompleted 方法来结束异步操作的生存期。
- //为某个特定任务调用此方法后,再调用其相应的 AsyncOperation 对象会引发异常。
- }
- private void Work1ReportProgressCB(object state)
- {
- Work1ProgressChangedEventArgs e = state as Work1ProgressChangedEventArgs;
- OnWork1ProgressChanged(e);
- }
- private void Work1CompleteCB(object state)
- {
- Work1CompletedEventArgs e = state as Work1CompletedEventArgs;
- OnWork1Completed(e);
- }
- #region Work1的进度通知和任务完成的事件
- public event Work1ProgressChangedEventHandler Work1ProgressChanged;
- protected virtual void OnWork1ProgressChanged(Work1ProgressChangedEventArgs e)
- {
- Work1ProgressChangedEventHandler temp = this.Work1ProgressChanged;
- if (temp != null)
- {
- temp(this, e);
- }
- }
- public event Work1CompletedEventHandler Work1Completed;
- protected virtual void OnWork1Completed(Work1CompletedEventArgs e)
- {
- Work1CompletedEventHandler temp = this.Work1Completed;
- if (temp != null)
- {
- temp(this, e);
- }
- }
- #endregion
- #endregion
- /// <summary>
- /// 取消指定userState的任务执行
- /// </summary>
- /// <param name="userState"></param>
- public void CancelAsync(object userState)
- {
- AsyncOperation asyncOp = userStateToLifetime[userState] as AsyncOperation;
- if (asyncOp != null)
- {
- lock (userStateToLifetime.SyncRoot)
- {
- userStateToLifetime.Remove(userState);
- }
- }
- }
- /// <summary>
- /// 判断指定userState的任务是否已经被结束。返回值:true 已经结束; false 还没有结束
- /// </summary>
- /// <param name="userState"></param>
- /// <returns></returns>
- private bool TaskCanceled(object userState)
- {
- return (userStateToLifetime[userState] == null);
- }
- }
- public class Work1ProgressChangedEventArgs :ProgressChangedEventArgs
- {
- private int totalWork = 1;
- public Work1ProgressChangedEventArgs(int totalWork, int progressPercentage, object userState)
- : base(progressPercentage, userState)
- {
- this.totalWork = totalWork;
- }
- /// <summary>
- /// Work1的总工作量
- /// </summary>
- public int TotalWork
- {
- get
- {
- return totalWork;
- }
- }
- }
- public class Work1CompletedEventArgs : AsyncCompletedEventArgs
- {
- public Work1CompletedEventArgs(Exception e, bool canceled, object state)
- : base(e, canceled, state)
- {
- }
- }
- }
C#中的异步调用及异步设计模式(三)——基于事件的异步模式的更多相关文章
-
【温故知新】C#基于事件的异步模式(EAP)
在开发winform和调用asp.net的web service引用的时候,会出现许多命名为 MethodNameAsync 的方法. 例如: winform的按钮点击 this.button1.Cl ...
-
二、基于事件的异步编程模式(EAP)
一.引言 在上一个专题中为大家介绍了.NET 1.0中提出来的异步编程模式--APM,虽然APM为我们实现异步编程提供了一定的支持,同时它也存在着一些明显的问题--不支持对异步操作的取消和没有提供对进 ...
-
C#中的异步调用及异步设计模式(二)——基于 IAsyncResult 的异步设计模式
三.基于 IAsyncResult 的异步设计模式(设计层面) IAsyncResult 异步设计模式通过名为 BeginOperationName 和 EndOperationName 的两个方法来 ...
-
在Silverlight中的DispatcherTimer的Tick中使用基于事件的异步请求
需求:在silverlight用户界面上使用计时器定时刷新数据. 在 Silverlight 中的 DispatcherTimer 的 Tick 事件 中使用异步请求数据时,会出现多次请求的问题,以下 ...
-
.NET - 基于事件的异步模型
注:这是大概四年前写的文章了.而且我离开.net领域也有四年多了.本来不想再发表,但是这实际上是Active Object模式在.net中的一种重要实现方法,因此我把它掏出来发布一下.如果该模型有新的 ...
-
基于事件的异步模式(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 ...
-
基于事件的异步模式——BackgroundWorker
实现异步处理的方法很多,经常用的有基于委托的方式,今天记录的是基于事件的异步模式.利用BackgroundWorker组件可以很轻松的实现异步处理,并且该组件还支持事件的取消.进度报告等功能.本文以计 ...
-
异步编程(二)基于事件的异步编程模式 (EAP)
一.引言 在上一个专题中为大家介绍了.NET 1.0中提出来的异步编程模式——APM,虽然APM为我们实现异步编程提供了一定的支持,同时它也存在着一些明显的问题——不支持对异步操作的取消和没有提供对进 ...
随机推荐
-
Python 随机数生成总结
random.uniform(a, b),返回[a,b]之间的浮点数 random.randint(a, b),返回[a,b]之间的整数 random.randrange([start], stop[ ...
-
Django教程:第一个Django应用程序(4)
Django教程:第一个Django应用程序(4) 2013-10-09 磁针石 #承接软件自动化实施与培训等gtalk:ouyangchongwu#gmail.comqq 37391319 #博客: ...
-
http://webhelp.esri.com/arcgisexplorer/2500/zh-CN/index.html#add_raster_data.htm
http://webhelp.esri.com/arcgisexplorer/2500/zh-CN/index.html#add_raster_data.htm
-
VM14安装Mas os 13
工具/原料 VMware Workstation unlocker(for OS X 插件补丁) macOS 10.13镜像 vmware tools 安装前准备 1 下载以上文件: 1. ...
-
python学习小总结(列表、元组、字典、集合、字符串)
---恢复内容开始--- 一.列表(list) 1.添加 append():追加,在列表末尾添加元素. 列表名.append(添加的元素) extend():扩展,在列表末尾添加元素. 列表名.ext ...
-
弹性盒子模型属性之flex-shrink
上一次,我们已经了解过flex-grow的具体用法后,这周,让我们一起来见一下flex-basis这个属性. flex-shrink 定义项目的缩小比例,默认值为1,注意前提是空间不足的情况下,项目缩 ...
-
LabelFunction 允许在显示数据以前进行处理
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="ht ...
-
c#中表达式的返回值是啥?
今天在学javascript的时候发现它其中有一个语法是这样的(a="haha).length;这样也能够输出haha这个字符串的长度,然而我在使用c#的时候分行读取txt文本的时候也用这个 ...
-
Pycharm 激活码 2019年1月9日16:57:13
K03CHKJCFT-eyJsaWNlbnNlSWQiOiJLMDNDSEtKQ0ZUIiwibGljZW5zZWVOYW1lIjoibnNzIDEwMDEiLCJhc3NpZ25lZU5hbWUiO ...
-
1136 A Delayed Palindrome
题意:略. 思路:大整数相加,回文数判断.对首次输入的数也要判断其是否是回文数,故这里用do...while,而不用while. 代码: #include <iostream> #incl ...