王燕丽

博客园 首页 联系 订阅 管理

看完上篇之后,我们知道await关键字后面跟的是一个Task对象,虽然为了看起来很像一个实际方法,Async CTP类库为现有对象添加的扩展方法没有使用典型的Get/Make等字眼,更准确的说法应该是await后面应该跟的是一个Await对象,或者是一个有一个public类型的GetAwaiter()方法并且能返回Awaiter对象的对象,这里所谓的Awaiter对象和我们以往的说法稍微有些区别,因为是编译器层的语法糖,所以Awaiter对象并没有实际的意义,比如实现了一个IAwaitable之类的接口,它仅仅要求有一个BeginAwait和EndAwait方法。

看下面的代码片段:

   1:  await 1000;

我们希望能异步等待1000毫秒,编译器当然毫不客气的拒绝了我们的要求,因为从1000这个数字无法获得一个Awaiter(为了简洁,后面沿用这个说法,准确的说法应该是没有从中找到BeginAwait和EndAwait方法),为了达到我们的目的,我们利用Task给int添加一个GetAwaiter方法:

   1:  public static class IntExtensions
   2:  {
   3:      public static TaskAwaiter<int> GetAwaiter(this int s)
   4:      {
   5:          TaskCompletionSource<int> tsc = new TaskCompletionSource<int>();
   6:          Task<int> task = tsc.Task;
   7:   
   8:          tsc.TrySetResult(0);
   9:   
  10:          return task.GetAwaiter<0>();
  11:      }
  12:  }

这样,await 1000被编译器认同,我们的代码能顺利执行,但是我们还没有为Task指定执行我们需要的等待1000毫秒,所以await会马上返回。这里先不涉及Task的内容,我们使用另外一个方法,直接为Int类型实现BeginAwait和EndAwait两个方法,或者单独定义一个IntAwaiter类,上面的代码简单的修改为:

   1:  public static IntAwaiter GetAwaiter(this int i)
   2:  {
   3:      return new IntAwaiter(i);
   4:  }

而IntAwaiter类的定义如下:

   1:  public class IntAwaiter
   2:  {
   3:      SynchronizationContext _UIContext = SynchronizationContext.Current;
   4:   
   5:      int _Value = 0;
   6:   
   7:      public IntAwaiter(int i)
   8:      {
   9:          _Value = i;
  10:      }
  11:   
  12:      public bool BeginAwait(Action continuation)
  13:      {
  14:          if (SynchronizationContext.Current != _UIContext)
  15:          {
  16:              return false;
  17:          }
  18:          else
  19:          {
  20:              ThreadPool.QueueUserWorkItem((state) =>
  21:              {
  22:                  Thread.Sleep(_Value);
  23:                  _Value = 5;
  24:                  _UIContext.Send(new SendOrPostCallback((target) => { continuation(); }), null);
  25:              });
  26:              return true;
  27:          }
  28:      }
  29:   
  30:      public int EndAwait()
  31:      {
  32:          return _Value;
  33:      }
  34:  }

然后我们来写几句代码测试一下:

   1:  listBox1.Items.Add(DateTime.Now);
   2:  await 1000;
   3:  listBox1.Items.Add(DateTime.Now);
   4:  await 5000;
   5:  listBox1.Items.Add(DateTime.Now);

结果如下:

1

IntAwaiter非常简单,实际上Async CTP中的TaskAwaiter也基本上就做了这些事,在BeginAwait中启动Task,在EndAwait中判断Task状态,如果成功完成,返回Task的Result对象,如果是Cancel或者Fault,则抛出异常。

有了前文和本文的了解,实际上如果一个应用中的异步方法是自己实现的而非像WCF一样由VS自动生成的代理类,那么完全可以在代码中处理async达到和await一样简洁的代码风格而不是让编译器来干这事,毕竟如果不是真的无法取代我不愿意带上130K的这家伙,而且Preview版无法调试await也很让人头疼。

posted on 2011-04-01 15:26  王燕丽  阅读(1406)  评论(1)    收藏  举报