async 和 await 关键字

C# 5.0中引入了async 和 await。这两个关键字可让你更方便的按照同步的方式写出异步代码。也就是说使你更方便的异步编程。常规的写法格式以下:web

  
  
           
  
  
  1. var result = await expression
  2. statement(s); 

这种写法至关于: express

  
  
           
  
  
  1. var awaiter = expression.GetAwaiter(); 
  2. awaiter.OnCompleted (() => 
  3. var result = awaiter.GetResult(); 
  4. statement(s); 
  5. ); 

这里的expression一般是Task或者Task<TResult>,可是事实上能够本身定义一些可使用await的对象。可是要知足必定的条件。先看一个例子。 编程

  
  
           
  
  
  1. static void Main(string[] args) 
  2.        { 
  3.            DisplayPrimesCount(); 
  4.            Thread.Sleep(5000);//等待异步执行完成 
  5.        } 
  6.        static Task<int> GetPrimesCountAsync(int start, int count) 
  7.        { 
  8.            return Task.Run(() => 
  9.            ParallelEnumerable.Range(start, count).Count(n => 
  10.            Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0))); 
  11.        } 
  12.        static async void DisplayPrimesCount() 
  13.        { 
  14.            int result = await GetPrimesCountAsync(2, 1000000);//此处会阻塞 
  15.            Console.WriteLine(result); 
  16.        } 

这是比较常见的写法。asp.net

要异步执行GetPrimesCountAsync方法,首先GetPrimesCountAsync返回的必须是"可等待"对象。上述代码中GetPrimesCountAsync返回的是Task<int>类型。而后,使用await的方法必需要标注async关键字。而且能够看到await GetPrimesCountAsync(2, 1000000);这个语句返回的是int,而不是Task<int>。异步

上述代码用天然语言描述就是以下:async

当调用DisplayPrimesCount,DisplayPrimesCount会运行一个新的Task,这个task会计算素数的个数。完成后,会将计算所得的值返回,并将这个返回值放到Task对象中,而且返回给调用者。调用者得到这个Task值后,取出Task的result值。ide

当程序逻辑遇到await GetPrimesCountAsync方法,线程就会被挂起,直到异步运行完成,获得result值后,再会继续运行下去。异步编程

本质上说await和async的出现也只是一颗语法糖,可是这颗语法糖可使得异步编程更优雅,直接摒弃了原先EAP和APM这种处处BeginXXX,EndXXX的丑陋模式,提升了生产力。this

可使用await的方法,返回值必须是awaitable对象,自定义awaitable对象比较麻烦,一个对象必须知足下列条件才行:spa

  1. 必须有一个 GetAwaiter()方法,扩展方法或者实例方法均可以
  2. GetAwaiter() 方法返回值必须是awaiter对象。一个对象要成为awaiter对象必须知足下列条件:
    • 该对象实现接口 INotifyCompletion 或者ICriticalNotifyCompletion
    • 必须有 IsCompleted属性
    • 必须有 GetResult()方法,能够返回void或者其余返回值。

因为微软并未给出知足上述条件的接口,所以能够本身实现这样的接口。 

  
  
           
  
  
  1. public interface IAwaitable<out TResult> 
  2.     { 
  3.         IAwaiter<TResult> GetAwaiter(); 
  4.     } 
  5.     public interface IAwaiter<out TResult> : INotifyCompletion // or ICriticalNotifyCompletion 
  6.     { 
  7.         bool IsCompleted { get; } 
  8.         TResult GetResult(); 
  9.     } 

因为对于拉姆达表达式不能够直接使用await,所以能够经过编程,技巧性的实现这一功能。好比对某一个Func委托实现扩展方法,注意: 扩展方法必须在顶级静态类中定义。 

  
  
           
  
  
  1. public static class FuncExtensions 
  2.     { 
  3.         public static IAwaiter<TResult> GetAwaiter<TResult>(this Func<TResult> function) 
  4.         { 
  5.             return new FuncAwaiter<TResult>(function); 
  6.         } 
  7.     } 
  8.     public interface IAwaitable<out TResult> 
  9.     { 
  10.         IAwaiter<TResult> GetAwaiter(); 
  11.     } 
  12.     public interface IAwaiter<out TResult> : INotifyCompletion // or ICriticalNotifyCompletion 
  13.     { 
  14.         bool IsCompleted { get; } 
  15.         TResult GetResult(); 
  16.     } 
  17.     internal struct FuncAwaitable<TResult> : IAwaitable<TResult> 
  18.     { 
  19.         private readonly Func<TResult> function; 
  20.         public FuncAwaitable(Func<TResult> function) 
  21.         { 
  22.             this.function = function; 
  23.         } 
  24.         public IAwaiter<TResult> GetAwaiter() 
  25.         { 
  26.             return new FuncAwaiter<TResult>(this.function); 
  27.         } 
  28.     } 
  29.     public struct FuncAwaiter<TResult> : IAwaiter<TResult> 
  30.     { 
  31.         private readonly Task<TResult> task; 
  32.         public FuncAwaiter(Func<TResult> function) 
  33.         { 
  34.             this.task = new Task<TResult>(function); 
  35.             this.task.Start(); 
  36.         } 
  37.         bool IAwaiter<TResult>.IsCompleted 
  38.         { 
  39.             get 
  40.             { 
  41.                 return this.task.IsCompleted; 
  42.             } 
  43.         } 
  44.         TResult IAwaiter<TResult>.GetResult() 
  45.         { 
  46.             return this.task.Result; 
  47.         } 
  48.         void INotifyCompletion.OnCompleted(Action continuation) 
  49.         { 
  50.             new Task(continuation).Start(); 
  51.         } 
  52.     } 
  53.      
  54. 在main中能够以下写: 
  55.  
  56.   static void Main(string[] args) 
  57.         { 
  58.             Func(() => { Console.WriteLine("await..");return 0;}); 
  59.             Thread.Sleep(5000);//等待异步执行完成 
  60.         } 
  61.         static async void Func(Func<int> f) 
  62.         { 
  63.             int result = await new Func<int>(f); 
  64.             Console.WriteLine(result); 
  65.         } 

其中:

Func方法能够异步执行了,由于Func<int>已经实现扩展方法GetAwaiter,而且返回值类型是本身定义的IAwaitable类型。

固然,更加简单的方法是,采用微软提供的Task对象,让拉姆达表达式返回Task类型就能够了。 

  
  
           
  
  
  1. static void Main(string[] args) 
  2.         { 
  3.             Func(() => 
  4.             { 
  5.                 return Task<int>.Run<int>(() => { return Enumerable.Range(1,100).Sum(); }); 
  6.             }); 
  7.             Thread.Sleep(5000);//等待异步执行完成 
  8.         } 
  9.         static async void Func(Func<Task<int>> f) 
  10.         { 
  11.             int result = await f(); 
  12.             Console.WriteLine(result); 
  13.         } 
---------------------------------

参考资料:《C# 5.0 IN A NUTSHELL》

http://weblogs.asp.net/dixin/archive/2012/11/08/understanding-c-async-await-2-awaitable-awaiter-pattern.aspx

相关文章
相关标签/搜索