看完上篇之后,我们知道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);
结果如下:
IntAwaiter非常简单,实际上Async CTP中的TaskAwaiter也基本上就做了这些事,在BeginAwait中启动Task,在EndAwait中判断Task状态,如果成功完成,返回Task的Result对象,如果是Cancel或者Fault,则抛出异常。
有了前文和本文的了解,实际上如果一个应用中的异步方法是自己实现的而非像WCF一样由VS自动生成的代理类,那么完全可以在代码中处理async达到和await一样简洁的代码风格而不是让编译器来干这事,毕竟如果不是真的无法取代我不愿意带上130K的这家伙,而且Preview版无法调试await也很让人头疼。

浙公网安备 33010602011771号