Q.C人生

君子博学而日参省乎己,则知明而行无过矣

导航

 

 Figure 1 IAsyncResult

 

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

}

 

 

When any BeginXxx method is called, this method must internally construct an object whose type implements IAsyncResult and its four read-only properties. This object identifies the status of the asynchronous operation that has just been started. After the BeginXxx method returns to application code, the application can query these properties to determine whether the operation has completed. This object also contains the state of the completed operation: a result value if the operation completed successfully, or an exception if the operation failed to complete. An application passes the IAsyncResult object to an EndXxx method, which waits for the operation to complete (assuming it's not finished yet). The EndXxx method either returns the result or throws the exception letting the caller know the operation's result or error.

Beginxxx方法被调用,这个方法内部必须建立一个实现IAsyncResult接口的对象.这个对象刚运行的初期能够获得异步操作的各种状态,Beginxxx方法返回后,应用程序可以查询操作是否完成,这个对象也包含了操作完成的状态,如果成功完成,返回完成结果,如果完成失败则抛出异常,应用程序可以传递IAsyncResult对象给Endxxx方法,Endxxx方法或者返回操作结果或者throw exception,来让调用者清楚操作结果或错误.

Figure 2 defines an AsyncResultNoResult class that implements the IAsyncResult interface. This simple class can be used for asynchronous operations that have no return value-specifically, the operation either succeeds or fails. The Stream's BeginWrite and EndWrite methods are an example of this. When you start an asynchronous write operation to a stream, the result is either success or failure-the EndWrite method is prototyped as returning void.

Figure 2 定义了一个实现IAsyncResult接口的AsyncResultNoResult.这个简单的类能够被用作异步操作且没有返回值的情况,操作可以成功或是不.流的BeginWriteEndWrite方法就是这种方法,你启动一个异步写操作到流中,结果可能是成功或是失败,EndWrite方法的原型返回时空.

 Figure 2 AsyncResultNoResult Class

 

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

}

 

 

As you can see, the AsyncResultNoResult class has a constructor that accepts AsyncCallback and Object arguments that are used to start all asynchronous operations. The constructor just saves these arguments in private fields. IAsyncResult's AsyncState property returns the Object field to the caller. The class defines an m_CompletedState field used to implement IAsyncResult's IsCompleted and CompletedSynchronously properties. It also defines an m_AsyncWaitHandle field to implement IAsyncResult's AsyncWaitHandle property. Finally, the class defines an m_exception field. This field is set when the operation completes. If the operation completes successfully, then the field is set to null (same as its initialized value); if the operation fails, then the field is set to the Exception-derived object indicating the reason for failure.

 

如你所见,AsyncResultNoResult类的构造函数接受AsyncCallbackObject类型的参数,用来启动所有的异步操作.构造函数仅仅保存这些参数到私有字段中.IAsyncResult接口的Asyncstate属性返回object(Object m_AsyncState)字段给调用者.该类定义了一个m_CompletedState字段用来实现IAsyncResult接口的IsCompletedCompleteSYnchronouly属性.该类也定义了M_AsyncWaitHandle字段来实现IAsyncResult接口的AsyncWaitHandle属性.最后,类定义了一个m_exception字段.这个字段在操作完成时被设置.如果操作成功,那么字段设置为null(同初始值相同);如果操作失败,那么字段设置为Excepion对象来说明失效原因.

 

If you analyze the AsyncResultNoResult class, you'll notice that the code as a whole is very straightforward-except for the section that deals with the m_AsyncWaitHandle field. This field, a reference to a ManualResetEvent object, is only needed if the code starting the asynchronous operation queries the AsyncWaitHandle property or if the code calls the EndInvoke method before the operation has actually completed executing. The most common and recommended way to use the APM is to specify an AsyncCallback method that should automatically be invoked when the operation completes. For this common usage, the ManualResetEvent object is not necessary at all. As a result(因此), I've gone to great lengths(竭尽全力) to avoid creating and using this object unless the code using an AsyncResultNoResult object absolutely needs it.

如果你分析AsyncResultNoResult,你会发现整个代码非常简单(Straightforward),除了处理m_AsyncWaitHandle的程序片.这个字段是一个ManualResetEvent对象,只有在异步操作中查询AsyncWaitHandle属性,或者在异步操作实际完成前调用EndInvoke.通常和推荐方式使用APM是指定AsyncCallback方法,可以在操作完成时自动调用.对于通常使用,ManualResetEvent对象根本没有必要.因此,我竭尽全力避免建立和使用这个对象,除非代码使用AsyncResultNoResult对象必须使用它.

 

The reason I go to such great lengths is because creating and using a kernel object (such as a ManualResetEvent) is relatively expensive. For more information about the performance hit of using kernel objects, please see my October 2005 Concurrent Affairs column. When the asynchronous operation completes, some piece of code must call AsyncResultNoResult's SetAsCompleted method, passing in null if the operation completed successfully or a reference to an Exception-derived object if the operation failed. The code also indicates whether the operation completed synchronously (almost never) or asynchronously (almost always). This information is returned from IAsyncResult's CompletedSynchronously property, but very few applications actually care about it.

   花费如此大力气如此的原因是建立和使用了kernel对象(:ManualResetEvent) 这是相当昂贵的.为了获得更多信息关于使用kernel对象性能,请看我在058月的 Concurrent Affairs专栏.当异步操作完成代码必须调用AsyncResultNoResultSetAsCompleted 方法,如果操作成功完成返回null 或者失败时返回一个Exception的引用.代码页指明了操作是同步完成(几乎不用)或异步完成(几乎总是).这个信息会通过IAsyncResult’sCompletedSynchronously属性返回,但是很少会有程序实际关注它.

 

Internally, SetAsCompleted saves the exception in the m_exception field and changes the state of the m_completedSynchronously field. Then, if the manual reset event object was created, it is set. Finally, if an AsyncCallback method was specified when the AsyncResultNoResult object was constructed, this method is called back, letting the application code know that the asynchronous operation completed so that it can process the result (or failure).

SetAsCompleted内部保存异常在m_exception字段并且修改m_completedSynchronously字段状态.然后,如果Manualreset Event对象建立了,使用set()方法取消线程阻断.最好,如果在AsyncResultNoResult建立时指定了AsyncCallback 方法,这个方法将会被回调,让调用代码知道异步操作完成了可以出来运行结果或处理异常了.

 

To get the results of the operation, the application code will call some EndXxx method that will, in turn, call AsyncResultNoResult's EndInvoke method to determine whether the operation succeeded. If EndInvoke is called prior to the operation completing, then EndInvoke uses the manual reset event to suspend the calling thread until the operation has completed. If the operation completes, EndInvoke either returns or throws the exception that was saved off earlier when SetAsCompleted was called.

 为了获得操作结果,调用代码将依次一些调用EndXX方法,调用AsyncResultNoResult::EndInvoke 可以确定操作是否成功,如果EndInvoke在操作完成之前被线程调用,那么EndInvoke使用WaitHandle(manual reset Event) 挂起线程知道操作完成,如果操作完成EndInvoke将会返回操作结果和throw出先前通过AsyncResultNoResult::SetAsCompleted调用保存的异常.

Since many asynchronous operations do have a return value, I also defined a class to support this: AsyncResult<TResult> (see Figure 3). This generic class is derived from AsyncResultNoResult and really just adds support for a return value of type TResult. This support comes in the form of a private field to hold the result (m_result), an overload of the SetAsCompleted method that accepts a TResult value, and a new EndInvoke method that waits for the operation to complete and then returns the result if the operation succeeded or throws an exception if the operation failed.

 

      由于很多异步操作有返回值,我也定义了一个类来支持这种要求:AsyncResult<Tresult>.这个泛型类派生至AsyncResultNoResult并且增加了对通用类型(Tresult)的返回值的支持.这种支持源自保存结果的私有变量(m_result),并且重载了SetAsCompleted方法,接受TResult 类型的形参,并且重新引入了一个EndInvoke方法,可以等待操作完成,然后如果操作成功返回结果,失败throw出异常.

Figure 3 AsyncResult with a Return Value

 

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)

   }

}

 

 

Also, many BeginXxx methods accept arguments in addition to the AsyncCallback and the Object. For example, the Socket class has a BeginAccept method that takes IPAddress (address) and Int32 (port) arguments. If you want to use the AsyncResultNoResult or AsyncResult<TResult> class with a BeginXxx method that takes additional arguments, you'll want to define your own type derived from either of these two base classes (depending on whether your EndXxx method returns void). In your class, define additional fields-one for each argument-and set them in your class's constructor. Then the method that does the actual work can extract these argument values from your class's fields when the time is right.

同样,许多Benginxxx方法也需要接受一个附加的参数AsyncCallback和一个状态对象.例如,Socket类有一个BeginAccept方法接受IP地址(IPAddress)和端口(int32)参数.如果你想使用AsyncResultNoResult或者AsynResult<Tresult>类并且带有附加的参数,你应该定义你自己派生自这两个类的类型(区别依赖于你的Endxxx方法是否返回null).在你的类中,定义附属字段并在构造函数中设置.那么在正确的时候你就可以实际使用这些值了.