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

相關文章
相關標籤/搜索