Kevin-moon

学习在于分享
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

深入APM,实现自己的APM

Posted on 2009-02-23 08:41  Kevin-moon  阅读(3251)  评论(21编辑  收藏  举报

     APM(Asynchronous Programming Model),异步编程模型。
     大家对这个应该不会陌生,甚至太过于熟悉了吧!因为在太多的地方看到过它,对于应用上也许更没的说了!呵呵。
     Control:BeginInvoke、EndInvoke
     Stream:BeginWrite、EndWrite
     ......
     上面这些都是FCL已经提供的可以使用APM的类,不过可能在一些特定的情况下:

1、构建和一些硬件(FCL没有提供支持的)直接交互的类。
2、BeginXXX、EndXXX这些都不是相同的名称,希望让调用者使用起来更加简单,对于这些现有的异步类进行下抽象。
3、在自定义的类上执行某些方法也许需要很多的时间,这些希望对这些方法进行异步处理,所以也加上APM这种方式。
4、一些Win32的函数(Win32注册表、日志...)目前只提供了同步处理,所以当使用这些的时候,希望对这些函数做些封装,加上异步支持。
............
     
     相信大家现在都明白为什么要自己的APM了吧!OK,那就让我们开始吧!


APM的核心(IAsyncResult)
     IAsyncResult接口是APM机制的核心:

public interface IAsyncResult {
   WaitHandle AsyncWaitHandle { 
get; } // For Wait-Until-Done technique
   Boolean    IsCompleted     { get; } // For Polling technique
   Object     AsyncState      { get; } // For Callback technique
   Boolean    CompletedSynchronously { get; } // Almost never used
}
     当任何一个BeginXXX方法被调用的时候,都会返回一个继承于该接口的对象。该对象可以用来监视异步动作的执行,例如,当调用BeginXXX后,我们可以通过该对象的属性判断异步动作是否完成,同时如果异步执行的时候有异常,那么就会将异常信息传给EndXXX方法,EndXXX方法等待异步动作执行完成后,将异常抛出。
     AsyncResultNoResult类继承于IAsyncResult接口,该类用于无特殊返回值的异步动作(执行成功或失败),例如stream的BeginWrite、EndWrite。
     public override void EndWrite(IAsyncResult asyncResult)

AsyncResultNoResult类:
Code
     详细分析下,AsyncResultNoResult的构造函数接受两个参数(异步委托和执行参数),这两个参数被保存到私有属性中。定义了m_CompletedState字段,用于实现IAsyncResult的 IsCompleted和CompletedSynchronously属性,也定义了m_AsyncWaitHandle字段用于实现IAsyncResulte的AsyncWaitHandle,而且还有一个异常字段m_exception,当成功执行的时候,把该值设为null,失败的时候,将被设置导致失败的异常。
     还有一个重要的属性(m_AsyncWaitHandle),我们在EndInvoke方法可以看到m_AsyncWaitHandle.WaitOne()这行代码,执行到这里的时候,m_AsyncWaitHandle如果没有被释放的话,程序将一直等待,释放的程序在SetAsCompleted方法中m_AsyncWaitHandle.Set()。

AsyncResult类:
Code
     看下代码应该很清晰了,该类继承与AsyncResultNoResult类,额外加了m_result属性,用于返回值。

利用自定义的IAsyncResult实现APM

     现在我们已经知道如何定义IAsyncResult,那么就开始展示如何使用AsyncResult<TResult>和AsyncResultNoResult。
     定义一个类:LongTask,该类对一些很耗时的方法进行异步处理,并且当执行完成的时候返回一个时间数据。
Code
     当调用BeginDoTask的时候,先实例化一个AsyncResult对象,然后把DoTaskHelper方法加到线程池上,而DoTaskHelper方法只是对执行动作进行了封装,如果该方法成功执行,则返回时间数据,如果出现异常,则设定异常信息,用于EndDoTask的时候抛出。

测试和性能
1、测试LongTask
Code
执行结果如下:


2、比较自定义APM和delegate之间的性能
Code
执行结果如下:

     从结果上看,既然自定义的APM性能要比FCL的APM好点,其实在这里大家仔细看下就可以清楚:
     区别就在于ManualResetEvent,FCL的APM是在创建IAsyncResult对象的同时,也创建了ManualResetEvent。而AsyncResultNoResult中,可以看下面的代码,只有在真正需要用到ManualResetEvent的时候,才去创建,一般不实例化。有个注意的地方:ManualResetEvent是内核对象,而创建或使用一个内核对象都是很耗资源的,所以只有当真正需要的时候才去使用。
public WaitHandle AsyncWaitHandle
   {
      
get
      {
         
if (m_AsyncWaitHandle == null)
         {
            Boolean done 
= IsCompleted;
            ManualResetEvent mre 
= new ManualResetEvent(done);
            
if (Interlocked.CompareExchange(ref m_AsyncWaitHandle, 
               mre, 
null!= null)
            {
               mre.Close();
            }
            
else
            {
               
if (!done && IsCompleted)
               {
                  m_AsyncWaitHandle.Set();
               }
            }
         }
         
return m_AsyncWaitHandle;
      }


参考Jeffrey Richter的
http://msdn.microsoft.com/en-us/magazine/cc163467.aspx