APM(Asynchronous Programming Model),异步编程模型。大家对这个应该不会陌生,甚至太过于熟悉了吧!因为在太多的地方看到过它,对于应用上也许更没的说了!呵呵。不过如果遇到下面几种情况也要提供APM的时候
   1、构建和一些硬件(FCL没有提供支持的)直接交互的类。
   2、在自定义的类上执行某些方法也许需要很多的时间,这些希望对这些方法进行异步处理,所以也加上APM这种方式。
   3、一些Win32的函数(Win32注册表、日志...)目前只提供了同步处理,所以当使用这些的时候,希望对这些函数做些封装,加上异步支持。.....
    
     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
Code
internal class AsyncResultNoResult : IAsyncResult
{
   // Fields set at construction which never change while 
   // operation is pending
   private readonly AsyncCallback m_AsyncCallback;
   private readonly Object m_AsyncState;
   // Fields set at construction which do change after 
   // operation completes
   private const Int32 c_StatePending = 0;
   private const Int32 c_StateCompletedSynchronously = 1;
   private const Int32 c_StateCompletedAsynchronously = 2;
   private Int32 m_CompletedState = c_StatePending;
   // Field that may or may not get set depending on usage
   private ManualResetEvent m_AsyncWaitHandle;
   // Fields set when operation completes
   private Exception m_exception;
   public AsyncResultNoResult(AsyncCallback asyncCallback, Object state)
   {
      m_AsyncCallback = asyncCallback;
      m_AsyncState = state;
   }
   public void SetAsCompleted(
      Exception exception, Boolean completedSynchronously)
   {
      // Passing null for exception means no error occurred. 
      // This is the common case
      m_exception = exception;
      // The m_CompletedState field MUST be set prior calling the callback
      Int32 prevState = Interlocked.Exchange(ref m_CompletedState,
         completedSynchronously ? c_StateCompletedSynchronously : 
         c_StateCompletedAsynchronously);
      if (prevState != c_StatePending)
         throw new InvalidOperationException(
             "You can set a result only once");
      // If the event exists, set it
      if (m_AsyncWaitHandle != null) m_AsyncWaitHandle.Set();
      // If a callback method was set, call it
      if (m_AsyncCallback != null) m_AsyncCallback(this);
   }
   public void EndInvoke()
   {
      // This method assumes that only 1 thread calls EndInvoke 
      // for this object
      if (!IsCompleted)
      {
         // If the operation isn't done, wait for it
         AsyncWaitHandle.WaitOne();
         AsyncWaitHandle.Close();
         m_AsyncWaitHandle = null;  // Allow early GC
      }
      // Operation is done: if an exception occured, throw it
      if (m_exception != null) throw m_exception;
   }
   #region Implementation of IAsyncResult
   public Object AsyncState { get { return m_AsyncState; } }
   public Boolean CompletedSynchronously {
      get { return Thread.VolatileRead(ref m_CompletedState) == 
                c_StateCompletedSynchronously; }
   }
   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)
            {
               // Another thread created this object's event; dispose 
               // the event we just created
               mre.Close();
            }
            else
            {
               if (!done && IsCompleted)
               {
                  // If the operation wasn't done when we created 
                  // the event but now it is done, set the event
                  m_AsyncWaitHandle.Set();
               }
            }
         }
         return m_AsyncWaitHandle;
      }
   }
   public Boolean IsCompleted {
      get { return Thread.VolatileRead(ref m_CompletedState) != 
                c_StatePending; }
   }
   #endregion
}
     还有一个重要的属性(m_AsyncWaitHandle),我们在EndInvoke方法可以看到m_AsyncWaitHandle.WaitOne()这行代码,执行到这里的时候,m_AsyncWaitHandle如果没有被释放的话,程序将一直等待,释放的程序在SetAsCompleted方法中m_AsyncWaitHandle.Set()。
AsyncResult类:

 Code
Code
internal class AsyncResult<TResult> : AsyncResultNoResult
{
   // Field set when operation completes
   private TResult m_result = default(TResult);
   public AsyncResult(AsyncCallback asyncCallback, Object state) : 
      base(asyncCallback, state) { }
   public void SetAsCompleted(TResult result, 
      Boolean completedSynchronously)
   {
      // Save the asynchronous operation's result
      m_result = result;
      // Tell the base class that the operation completed 
      // sucessfully (no exception)
      base.SetAsCompleted(null, completedSynchronously);
   }
   new public TResult EndInvoke()
   {
      base.EndInvoke(); // Wait until operation has completed 
      return m_result;  // Return the result (if above didn't throw)
   }
}
利用自定义的IAsyncResult实现APM
     现在我们已经知道如何定义IAsyncResult,那么就开始展示如何使用AsyncResult<TResult>和AsyncResultNoResult。
     定义一个类:LongTask,该类对一些很耗时的方法进行异步处理,并且当执行完成的时候返回一个时间数据。

 Code
Code
internal sealed class LongTask
{
   private Int32 m_ms;  // Milliseconds;
   public LongTask(Int32 seconds)
   {
      m_ms = seconds * 1000;
   }
   // Synchronous version of time-consuming method
   public DateTime DoTask()
   {
      Thread.Sleep(m_ms);  // Simulate time-consuming task
      return DateTime.Now; // Indicate when task completed
   }
   // Asynchronous version of time-consuming method (Begin part)
   public IAsyncResult BeginDoTask(AsyncCallback callback, Object state)
   {
      // Create IAsyncResult object identifying the 
      // asynchronous operation
      AsyncResult<DateTime> ar = new AsyncResult<DateTime>(
         callback, state);
      // Use a thread pool thread to perform the operation
      ThreadPool.QueueUserWorkItem(DoTaskHelper, ar);
      return ar;  // Return the IAsyncResult to the caller
   }
   // Asynchronous version of time-consuming method (End part)
   public DateTime EndDoTask(IAsyncResult asyncResult)
   {
      // We know that the IAsyncResult is really an 
      // AsyncResult<DateTime> object
      AsyncResult<DateTime> ar = (AsyncResult<DateTime>)asyncResult;
      // Wait for operation to complete, then return result or 
      // throw exception
      return ar.EndInvoke();
   }
   // Asynchronous version of time-consuming method (private part 
   // to set completion result/exception)
   private void DoTaskHelper(Object asyncResult)
   {
      // We know that it's really an AsyncResult<DateTime> object
      AsyncResult<DateTime> ar = (AsyncResult<DateTime>)asyncResult;
      try
      {
         // Perform the operation; if sucessful set the result
         DateTime dt = DoTask();
         ar.SetAsCompleted(dt, false);
      }
      catch (Exception e)
      {
         // If operation fails, set the exception
         ar.SetAsCompleted(e, false);
      }
   }
}
测试和性能
1、测试LongTask

 Code
Code
private static void FunctionalTest()
{
  IAsyncResult ar;
  LongTask lt = new LongTask(5);
  // Prove that the Wait-until-done technique works
  ar = lt.BeginDoTask(null, null);
  Console.WriteLine("Task completed at: {0}", lt.EndDoTask(ar));
  // Prove that the Polling technique works
  ar = lt.BeginDoTask(null, null);
  while (!ar.IsCompleted)
  {
     Console.WriteLine("Not completed yet.");
     Thread.Sleep(1000);
  }
  Console.WriteLine("Task completed at: {0}", lt.EndDoTask(ar));
  // Prove that the Callback technique works
  lt.BeginDoTask(TaskCompleted, lt);
  Console.ReadLine();
}
private static void TaskCompleted(IAsyncResult ar)
{
  LongTask lt = (LongTask)ar.AsyncState;
  Console.WriteLine("Task completed at: {0}", lt.EndDoTask(ar));
  Console.WriteLine("All done, hit Enter to exit app.");
} 2、比较自定义APM和delegate之间的性能
2、比较自定义APM和delegate之间的性能

 Code
Code
private const Int32 c_iterations = 100 * 1000; // 100 thousand
private static Int32 s_numDone;
private delegate DateTime DoTaskDelegate();
private static void PerformanceTest()
{
   AutoResetEvent are = new AutoResetEvent(false);
   LongTask lt = new LongTask(0);
   Stopwatch sw;
   s_numDone = 0;
   sw = Stopwatch.StartNew();
   for (Int32 n = 0; n < c_iterations; n++)
   {
      lt.BeginDoTask(delegate(IAsyncResult ar)
      {
         if (Interlocked.Increment(ref s_numDone) == c_iterations)
            are.Set();
      }, null);
   }
   are.WaitOne();
   Console.WriteLine("AsyncResult Time: {0}", sw.Elapsed);
   s_numDone = 0;
   DoTaskDelegate doTaskDelegate = lt.DoTask;
   sw = Stopwatch.StartNew();
   for (Int32 n = 0; n < c_iterations; n++)
   {
      doTaskDelegate.BeginInvoke(delegate(IAsyncResult ar)
      {
         if (Interlocked.Increment(ref s_numDone) == c_iterations)
            are.Set();
      }, null);
   }
   are.WaitOne();
   Console.WriteLine("Delegate    Time: {0}", sw.Elapsed);
}

     从结果上看,既然自定义的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