四、基于事件的异步模式(设计层面)

基于事件的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方法的实现:

  1. /// <summary>
  2. /// AsyncOperation类型的Post方法的实现
  3. /// </summary>
  4. public void Post(SendOrPostCallback d, object arg)
  5. {
  6. this.VerifyNotCompleted();
  7. this.VerifyDelegateNotNull(d);
  8. this.syncContext.Post(d, arg);
  9. }

在AsyncOperation类型的Post方法中,直接调用了SynchronizationContext类型的Post方法,再看该Post方法的实现:

  1. /// <summary>
  2. /// WindowsFormsSynchronizationContext类型的Post方法的实现
  3. /// </summary>
  4. public override void Post(SendOrPostCallback d, object state)
  5. {
  6. if (this.controlToSendTo != null)
  7. {
  8. this.controlToSendTo.BeginInvoke(d, new object[] { state }); //此处保证了SendOrPostCallBack委托在UI线程上执行
  9. }
  10. }

有以上三个类型(AsyncOpertion,AsyncOperationManager和SynchronizationContext)作为基础,实现基于事件的异步模式的进度通知和完成通知就轻松多了。下面用一个基于事件的异步模型的例子来结束本文章。

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Text;
    4. using System.ComponentModel;
    5. using System.Collections.Specialized;
    6. using System.Threading;
    7. namespace test
    8. {
    9. /// <summary>
    10. /// 任务1的进度通知代理
    11. /// </summary>
    12. /// <param name="sender"></param>
    13. /// <param name="e"></param>
    14. public delegate void Work1ProgressChangedEventHandler(object sender, Work1ProgressChangedEventArgs e);
    15. /// <summary>
    16. /// 任务1的进度通知参数
    17. /// </summary>
    18. /// <param name="sender"></param>
    19. /// <param name="e"></param>
    20. public delegate void Work1CompletedEventHandler(object sender, Work1CompletedEventArgs e);
    21. public class BasedEventAsyncWorker
    22. {
    23. private delegate void WorkerEventHandler(int maxNumber, AsyncOperation asyncOp);
    24. private HybridDictionary userStateToLifetime = new HybridDictionary();
    25. public BasedEventAsyncWorker()
    26. { }
    27. #region DoWork1的基于事件的异步调用
    28. public void DoWork1Async(object userState, int maxNumber)
    29. {
    30. AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(userState);
    31. //userStateToLifetime有可能会同时被多线程访问,在此需要lock进行同步处理
    32. lock (userStateToLifetime.SyncRoot)
    33. {
    34. if (userStateToLifetime.Contains(userState))
    35. {
    36. throw new ArgumentException(
    37. "userState parameter must be unique",
    38. "userState");
    39. }
    40. userStateToLifetime[userState] = asyncOp;
    41. }
    42. //异步开始任务1
    43. WorkerEventHandler workerDelegate = new WorkerEventHandler(DoWork1);
    44. workerDelegate.BeginInvoke(maxNumber, asyncOp, null, null);
    45. }
    46. private void DoWork1(int maxNumber, AsyncOperation asyncOp)
    47. {
    48. Exception e = null;
    49. //判断该userState的任务仍在处理中
    50. if (!TaskCanceled(asyncOp.UserSuppliedState))
    51. {
    52. try
    53. {
    54. int n = 0;
    55. int percentage = 0;
    56. while (n < maxNumber && !TaskCanceled(asyncOp.UserSuppliedState))
    57. {
    58. Thread.Sleep(100); //模拟耗时操作
    59. percentage = (int)((float)n / (float)maxNumber * 100);
    60. Work1ProgressChangedEventArgs progressChanageArgs =
    61. new Work1ProgressChangedEventArgs(maxNumber, percentage, asyncOp.UserSuppliedState);
    62. //任务1的进度通知
    63. asyncOp.Post(new SendOrPostCallback(Work1ReportProgressCB), progressChanageArgs);
    64. n++;
    65. }
    66. }
    67. catch (Exception ex)
    68. {
    69. e = ex;
    70. }
    71. }
    72. this.Work1Complete(e, TaskCanceled(asyncOp.UserSuppliedState), asyncOp);
    73. }
    74. private void Work1Complete(Exception exception, bool canceled, AsyncOperation asyncOp)
    75. {
    76. if (!canceled)
    77. {
    78. lock (userStateToLifetime.SyncRoot)
    79. {
    80. userStateToLifetime.Remove(asyncOp.UserSuppliedState);
    81. }
    82. }
    83. Work1CompletedEventArgs e = new Work1CompletedEventArgs(exception, canceled, asyncOp.UserSuppliedState);
    84. //通知指定的任务已经完成
    85. asyncOp.PostOperationCompleted(new SendOrPostCallback(Work1CompleteCB), e);
    86. //调用 PostOperationCompleted 方法来结束异步操作的生存期。
    87. //为某个特定任务调用此方法后,再调用其相应的 AsyncOperation 对象会引发异常。
    88. }
    89. private void Work1ReportProgressCB(object state)
    90. {
    91. Work1ProgressChangedEventArgs e = state as Work1ProgressChangedEventArgs;
    92. OnWork1ProgressChanged(e);
    93. }
    94. private void Work1CompleteCB(object state)
    95. {
    96. Work1CompletedEventArgs e = state as Work1CompletedEventArgs;
    97. OnWork1Completed(e);
    98. }
    99. #region Work1的进度通知和任务完成的事件
    100. public event Work1ProgressChangedEventHandler Work1ProgressChanged;
    101. protected virtual void OnWork1ProgressChanged(Work1ProgressChangedEventArgs e)
    102. {
    103. Work1ProgressChangedEventHandler temp = this.Work1ProgressChanged;
    104. if (temp != null)
    105. {
    106. temp(this, e);
    107. }
    108. }
    109. public event Work1CompletedEventHandler Work1Completed;
    110. protected virtual void OnWork1Completed(Work1CompletedEventArgs e)
    111. {
    112. Work1CompletedEventHandler temp = this.Work1Completed;
    113. if (temp != null)
    114. {
    115. temp(this, e);
    116. }
    117. }
    118. #endregion
    119. #endregion
    120. /// <summary>
    121. /// 取消指定userState的任务执行
    122. /// </summary>
    123. /// <param name="userState"></param>
    124. public void CancelAsync(object userState)
    125. {
    126. AsyncOperation asyncOp = userStateToLifetime[userState] as AsyncOperation;
    127. if (asyncOp != null)
    128. {
    129. lock (userStateToLifetime.SyncRoot)
    130. {
    131. userStateToLifetime.Remove(userState);
    132. }
    133. }
    134. }
    135. /// <summary>
    136. /// 判断指定userState的任务是否已经被结束。返回值:true 已经结束; false 还没有结束
    137. /// </summary>
    138. /// <param name="userState"></param>
    139. /// <returns></returns>
    140. private bool TaskCanceled(object userState)
    141. {
    142. return (userStateToLifetime[userState] == null);
    143. }
    144. }
    145. public class Work1ProgressChangedEventArgs :ProgressChangedEventArgs
    146. {
    147. private int totalWork = 1;
    148. public Work1ProgressChangedEventArgs(int totalWork, int progressPercentage, object userState)
    149. : base(progressPercentage, userState)
    150. {
    151. this.totalWork = totalWork;
    152. }
    153. /// <summary>
    154. /// Work1的总工作量
    155. /// </summary>
    156. public int TotalWork
    157. {
    158. get
    159. {
    160. return totalWork;
    161. }
    162. }
    163. }
    164. public class Work1CompletedEventArgs : AsyncCompletedEventArgs
    165. {
    166. public Work1CompletedEventArgs(Exception e, bool canceled, object state)
    167. : base(e, canceled, state)
    168. {
    169. }
    170. }
    171. }

C#中的异步调用及异步设计模式(三)——基于事件的异步模式的更多相关文章

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

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

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

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

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

    三.基于 IAsyncResult 的异步设计模式(设计层面) IAsyncResult 异步设计模式通过名为 BeginOperationName 和 EndOperationName 的两个方法来 ...

  4. 在Silverlight中的DispatcherTimer的Tick中使用基于事件的异步请求

    需求:在silverlight用户界面上使用计时器定时刷新数据. 在 Silverlight 中的 DispatcherTimer 的 Tick 事件 中使用异步请求数据时,会出现多次请求的问题,以下 ...

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. 在centos7上搭建mongodb副本集

    1.安装副本集介绍 副本集(Replica Set)是一组MongoDB实例组成的集群,由一个主(Primary)服务器和多个备份(Secondary)服务器构成.通过Replication,将数据的 ...

  2. EntityFramework 常见用法汇总

    1.Code First 启用存储过程映射实体 1 protected override void OnModelCreating(DbModelBuilder modelBuilder) 2 { 3 ...

  3. OD 实验(十二) - 对一个 Delphi 程序的逆向

    程序: 运行程序 界面显示的是未注册 点击 Help -> About 点击 Use Reg Key 这里输入注册码 用 PEiD 看一下 该程序是用 Delphi 6.0 - 7.0 写的 逆 ...

  4. OD 实验(二) - 绕过序列号验证

    需要破解的程序 输入用户名和序列号,点击 Check,程序会进行校验 用 OD 打开程序 按快捷键 Ctrl+F9 跟随表达式 GetDlgItemTextA 点击 ok 在这里调用了 GetDlgI ...

  5. linux中的常用压缩与解压缩命令

    linux中常用的压缩格式有   .zip   .gz   .bz2   .tar.gz   .tar.bz2 一..zip 1.命令格式 zip 压缩文件名 源文件名         (压缩文件到当 ...

  6. springMVC json自动将date类型转换为long

    今天早上遇到了一个奇怪得问题,直接给后台发送请求返回得页面信息中显示时间是正常得,如:2016-03-17 15:42:11.0,但是通过AJAX获取得信息中显示得时间竟然是时间戳. 我首先检查后台传 ...

  7. App压力测试背景

    开展压力测试 原因: 提高产品的稳定性 提高产品的留存率 时间: 首轮功能测试通过 夜间进行(将工具设置后进行,节约资源) 如何开展: 确定事件流 模拟事件流

  8. mysql 更改密码

    Mac 安装mysql时会生成一个默认密码: 这个可以在通知中找到,如果你需要更改密码则继续看下面, 今天给mac安装了MySQL,安装过程非常的顺利,但是在用一个可视化工具进行连接时,需要输入密码, ...

  9. From Ruby array to JS array in Rails- 'quote'?

    From Ruby array to JS array in Rails- 'quote'? <%= raw @location_list.as_json %>

  10. rtmp聊天相关归总

    使用共享对象(SharedObject)来开发时时文字聊天其实是很简单的. SharedObject可以跟踪和广播消息,连接到SharedObject中的其中任何一个客户端改变了SharedObjec ...