代码改变世界

Asynchronous Result 模式

2009-12-23 21:21  飞逝心情  阅读(237)  评论(0编辑  收藏  举报

异步结果模式提供了一种不必显式的调用Thread类来编写线程方法代码,而是使用委托实例的方式实现多线程处理的一种方法.

先来见识一下这种方法的标准代码:

public class AsyncResultPatternIntroduction
   {

       delegate void WorkerThreadHandler();

       public static AutoResetEvent ResetEvent =
          new AutoResetEvent(false);

       public static void Main()
       {
           Console.WriteLine("Application started....");

           WorkerThreadHandler workerMethod = null;
           IAsyncResult asyncResult = null;
           try
           {
               workerMethod =
                   new WorkerThreadHandler(DoWork);
               Console.WriteLine("Starting thread....");
             //执行操作,将结果保存在asyncResult中
               asyncResult =
                   workerMethod.BeginInvoke(null, null);

                // Display periods as progress bar.
               //间隔一段时间,查看线程是否结束,此循环在异步方结束
               while (!asyncResult.AsyncWaitHandle.WaitOne(100, false))
               {
                   Console.Write('.');
               }
               Console.WriteLine();
               Console.WriteLine("Thread ending....");
           }
           finally
           {
               //确保异步线程的调用结束
               if (workerMethod != null && asyncResult != null)
               {
                   workerMethod.EndInvoke(asyncResult);
               }
           }
           Console.WriteLine("Application shutting down....");
       }

       public static void DoWork()
       {
          //模仿一个需要长时间的操作
           Console.WriteLine("\nDoWork() started....");
           Thread.Sleep(6000);
           Console.WriteLine("\nDoWork() ending....");
       }
   }

上面的方法很好的做到了异步,但是,你没有办法来做到另外的一点,就是传入数据,做数据的交换。那么,含有参数的情况下怎么处理呢?假设我们有一个方法。

public static string[] GetFiles(
    string searchPattern, bool recurseSubdirectories)
  {
      //do action...
      //
  }

这个方法有两个参数,返回一个数组。为了传入参数,我们需要定义一个新的委托。

delegate string[] GetFilesHandler(
          string searchPattern, bool recurseSubdirectories);

有了这个委托后,我们可以按照上面的方法一样,声明一个实例,然后调用BeginInvoke()方法。不过需要注意的是这时候的BeginInvoke()方法需要传入四个参数。

result = asyncGetFilesHandler.BeginInvoke(
               searchPattern, recurseSubdirectories, null, null);

很简单的,前面的两个参数直接被传到方法中。

与BeginInove() 类似,由于我们的方法返回了值,在调用EndInove()方法时,可以获得方法的返回时。

// Retrieve the results
        files = (string[])asyncGetFilesHandler.EndInvoke(result);

注意,如果方法的名称标记为ref,或者out,则ref 参数和out 参数也应该存在于EndInvoke()的参数列表中

除了轮询,我们还可以采用通知机制,让它能在一个线程完成之后触发一个事件,这就是AsycCallBack模式,它会在方法结束后立即调用回调委托。

假设我们有一个方法名称叫SearchComplete,它用于处理当搜索文件结束时的操作。

public static void SearchCompleted()
       {
           //Write SearchedResult!
       }

如果我们想要在搜索完成时就要执行这个方法,我们就可以在BeginInvoke()方法的倒数第二个参数中传递要执行的函数。注意,此时被调和的参数需要包含参数列表。

public static void SearchCompleted(IAsyncResult result)
而我们的方法调用如下所示.
asyncGetFilesHandler.BeginInvoke(
                       arg, recurseSubdirectories,
                       SearchCompleted, null);

由于BeginInvoke方法的最后一个参数可传入方法的参数,因此,如果要向被调用的函数传入方法参数,则可以放到BeginInvoke()方法的最后一个参数中。由于是object因此,可以传递任意的类型。

传入的参数可以在调用方法时,以下面的方法取出来。

string searchPattern = (string)result.AsyncState;

一般来说,我们需要在回调函数中调用EndInvoke()方法,而不是放在Main()中,这是因为在调用EndInvoke()方法时会暂时阻止,直到异步调用完成,而在回调内则不存在这样的问题。这里是完善后的回调方法。分别实现了上面提到的几个功能。

public static void SearchCompleted(IAsyncResult result)
      {
          string searchPattern = (string)result.AsyncState;
          Console.WriteLine("{0}:", searchPattern);
          AsyncResult asyncResult = (AsyncResult)result;
          GetFilesHandler handler =
              (GetFilesHandler)asyncResult.AsyncDelegate;
          string[] files = handler.EndInvoke(result);
          foreach (string file in files)
          {
              Console.WriteLine("\t" + Path.GetFileName(file));
          }
      }

Asynchronous Result模式的一个关键特性是:调用者决定是否异步调用一个方法。被调用的对象可以选择提供异步方法,也可以在内部继续使用Asynchronous Result模式.被调用方法实现异步API的一个前提是被调用的类可以比调用者更为有效的实现异步功能,或者已经方法执行起来相当的慢。