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

时间:2022-08-27 13:50:06

http://www.cnblogs.com/fish-li/archive/2011/10/23/2222013.html


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

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

[csharp]  view plain  copy
  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方法的实现:

[csharp]  view plain  copy
  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.     }  
  11. }  


 

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

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