Q.C人生

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

导航

 

Implementing the APM

Now that you see how to define a type that implements the IAsyncResult interface, I'll show how to use my AsyncResult<TResult> and AsyncResultNoResult classes. I defined a LongTask class (see Figure 4) that offers a synchronous DoTask method that takes a long time to execute and returns a DateTime instance indicating when the operation completed.

到目前为止,你已经看到了如何定义一个类来实现IAsyncResult接口,接下来演示一下如何使用我的AsyncResult<TResult>AsyncResultNoResult.我定义了一个LongTask,该类提供了一个同步方法DoTask(),这个方法会消耗很长时间运行,并且返回一个DateTime实例标志何时运行完成.

 Figure 4 LongTask Simulates Asynchronous I/O

 

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);

      }

   }

}

 

 

As a convenience, I also offer BeginDoTask and EndDoTask methods that follow the CLR APM, allowing users to execute the DoTask method asynchronously. When a user calls my BeginDoTask method, I construct an AsyncResult<DateTime> object. Then I have a thread pool thread call a small helper method, DoTaskHelper, which wraps a call to the synchronous DoTask method.

为了方便起见,我同CLRAPM一样提供了BeginDoTaskEndDoTask方法,也许可用户直接在同步方式下运行DoTask.当一个用户调用BeginDoTask方法时,我构建了AsyncResult<DateTime>对象.然后我用线程池调用了DoTaskHelper辅助方法,这个方法包装了对DoTask方法的同步调用.

 

The DoTaskHelper method simply calls the synchronous version of the method with a try block. If the DoTask method runs to completion without failing (throwing an exception), then I call SetAsCompleted to set the operation's return value. If the DoTask method throws an exception, then DoTaskHelper's catch block will catch the exception and indicate that the operation has completed by calling SetAsCompleted, passing in the reference to the Exception-derived object.

DoTaskHelper方法在try块中简单的调用同步版本的方法.如果DoTask方法没有异常的运行完成,那么调用SetAsCompleted并且设置操作的返回值;反之DoTaskHelperCath块将会捕获异常并且通过调用传入异常对象的SetAsCompleted方法,同时标志操作完成.

The application code calls LongTask's EndDoTask method to get the results of the operation. All EndXxx methods are passed an IAsyncResult. Internally, the EndDoTask method knows that the IAsyncResult object passed to it is really an AsyncResult<DateTime> object, casts it, and calls EndInvoke on it. As discussed earlier, AsyncResult<TResult>'s EndInvoke method waits for the operation to complete (if necessary) and then returns the result or throws an exception indicating back to the caller the outcome of the asynchronous operation.

应用程序代码调用LongTask::EndDoTask方法来获得操作结果.所有的Endxxx方法都需要传递一个IAsyncResult对象.在内部EndDOTask方法知道IAsyncResult对象实际上是AsyncResult<DateTime>对象,结果转换,并在其上调用EndInvoke方法.向先前讨论过一样,AsyncResult<Tresult>::EndInvoke方法等待操作完成并返回结果或抛出异常,通知调用者异步操作完成.

Testing and Performance

The FunctionalTest method (see Figure 5) shows some code that uses my implementation of the APM. It tests the three rendezvous(聚会(地方); 约会地点[时间]; 公共场所; 人们常去(游憩)的地方;) techniques offered by the APM: wait until done, polling, and callback method. If you examine the code, you'll see that it looks identical to any other usage of the APM that you've ever seen. Of course, this is the point of the whole exercise.

FunctionTest方法演示一些实现APM的代码,测试APM提供的三种常用的技术:Wait until donepolling和回调方法.如果你测试一个代码,你将会看到这些APM用法基本相似,当然这仅仅是测试的一个方面.

 Figure 5 Using LongTask

 

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.");

}

 

 

The PerformanceTest method (see Figure 6) compares the performance of my IAsyncResult implementation to the implementation that the CLR provides when using a delegate's BeginInvoke and EndInvoke methods. My implementation seems to perform better than the FCL's current implementation, apparently due to it always constructing a ManualResetEvent whenever it creates its IAsyncResult object regardless of whether this event is needed by the application.

PerormanceTest方法比较了我的IASyncResult实现和ClR提供的托管的BeingInvokeEndInvoke方法的比较,我的实现看起来比基础类库(FCL)的当前实现性能要好一些,显然是由于FCL实现总是在构造IAsyncResult对象时建立ManualResetEvent对象,无论应用程序是否需要.

 

 Figure 6 Testing IAsyncResult Performance

 

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);

}

 

 

Conclusion

I think it is interesting to understand what is going on inside the CLR when we use mechanisms such as the APM. After examining my implementation here, you can get a sense of the size of IAsyncResult objects, what their state is and how they manage their state. This understanding can lead to improved ways of architecting your own applications and to better performance.

In this column, I used my IAsyncResult implementation to perform compute-bound tasks using thread pool threads. In a future column, I'll show how to use my IAsyncResult implementation with I/O-bound operations.

 

  我想当我们使APM,了解CLR的内部机制一定非常有趣.测试完我的实现代码后,你个可以获得一连串的IAsyncResult对象,不管他们的状态是什么或者如何管理他们的状态.明白了这些可以引导你提升程序的架构方式并且获得更好的性能.

  在这篇文章中我使用线程池来实现IAsyncResult去执行compute-bound 任务.未来的专栏将会为大家演示使用我的IAsyncResult实现完成I/O-bound操作.