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

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

基于事件的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 plaincopy
  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 plaincopy
  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 plaincopy
    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, null, null);  
    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.