C# 5.0中引入了async 和 await。這兩個關鍵字可以讓你更方便的按照同步的方式寫出異步代碼。也就是說使你更方便的異步編程。常規的寫法格式如下: var result = await expression; statement(s); 這種寫法相當於: var awaiter = expression.GetAwaiter(); awaiter.OnCompleted (() => { var result = awaiter.GetResult(); statement(s); ); 這裡的expression通常是Task或者Task<TResult>,但是事實上可以自己定義一些可以使用await的對象。但是要滿足一定的條件。先看一個例子。 static void Main(string[] args) { DisplayPrimesCount(); Thread.Sleep(5000);//等待異步執行完成 } static Task<int> GetPrimesCountAsync(int start, int count) { return Task.Run(() => ParallelEnumerable.Range(start, count).Count(n => Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0))); } static async void DisplayPrimesCount() { int result = await GetPrimesCountAsync(2, 1000000);//此處會阻塞 Console.WriteLine(result); } 這是比較常見的寫法。 要異步執行GetPrimesCountAsync方法,首先GetPrimesCountAsync返回的必須是"可等待"對象。上述代碼中GetPrimesCountAsync返回的是Task<int>類型。然後,使用await的方法必須要標注async關鍵字。並且可以看到await GetPrimesCountAsync(2, 1000000);這個語句返回的是int,而不是Task<int>。 上述代碼用自然語言描述就是如下: 當調用DisplayPrimesCount,DisplayPrimesCount會運行一個新的Task,這個task會計算素數的個數。完成後,會將計算所得的值返回,並將這個返回值放到Task對象中,並且返回給調用者。調用者獲得這個Task值後,取出Task的result值。 當程序邏輯遇到await GetPrimesCountAsync方法,線程就會被掛起,直到異步運行完成,得到result值後,再會繼續運行下去。 本質上說await和async的出現也只是一顆語法糖,但是這顆語法糖可以使得異步編程更優雅,直接摒棄了原先EAP和APM這種到處BeginXXX,EndXXX的丑陋模式,提高了生產力。 可以使用await的方法,返回值必須是awaitable對象,自定義awaitable對象比較麻煩,一個對象必須滿足下列條件才行: 必須有一個 GetAwaiter()方法,擴展方法或者實例方法都可以 GetAwaiter() 方法返回值必須是awaiter對象。一個對象要成為awaiter對象必須滿足下列條件: 該對象實現接口 INotifyCompletion 或者ICriticalNotifyCompletion 必須有 IsCompleted屬性 必須有 GetResult()方法,可以返回void或者其他返回值。 由於微軟並未給出滿足上述條件的接口,因此可以自己實現這樣的接口。 public interface IAwaitable<out TResult> { IAwaiter<TResult> GetAwaiter(); } public interface IAwaiter<out TResult> : INotifyCompletion // or ICriticalNotifyCompletion { bool IsCompleted { get; } TResult GetResult(); } 由於對於拉姆達表達式不可以直接使用await,因此可以通過編程,技巧性的實現這一功能。比如對某一個Func委托實現擴展方法,注意: 擴展方法必須在頂級靜態類中定義。 public static class FuncExtensions { public static IAwaiter<TResult> GetAwaiter<TResult>(this Func<TResult> function) { return new FuncAwaiter<TResult>(function); } } public interface IAwaitable<out TResult> { IAwaiter<TResult> GetAwaiter(); } public interface IAwaiter<out TResult> : INotifyCompletion // or ICriticalNotifyCompletion { bool IsCompleted { get; } TResult GetResult(); } internal struct FuncAwaitable<TResult> : IAwaitable<TResult> { private readonly Func<TResult> function; public FuncAwaitable(Func<TResult> function) { this.function = function; } public IAwaiter<TResult> GetAwaiter() { return new FuncAwaiter<TResult>(this.function); } } public struct FuncAwaiter<TResult> : IAwaiter<TResult> { private readonly Task<TResult> task; public FuncAwaiter(Func<TResult> function) { this.task = new Task<TResult>(function); this.task.Start(); } bool IAwaiter<TResult>.IsCompleted { get { return this.task.IsCompleted; } } TResult IAwaiter<TResult>.GetResult() { return this.task.Result; } void INotifyCompletion.OnCompleted(Action continuation) { new Task(continuation).Start(); } } 在main中可以如下寫: static void Main(string[] args) { Func(() => { Console.WriteLine("await..");return 0;}); Thread.Sleep(5000);//等待異步執行完成 } static async void Func(Func<int> f) { int result = await new Func<int>(f); Console.WriteLine(result); } 其中: Func方法可以異步執行了,因為Func<int>已經實現擴展方法GetAwaiter,並且返回值類型是自己定義的IAwaitable類型。 當然,更加簡單的方法是,采用微軟提供的Task對象,讓拉姆達表達式返回Task類型就可以了。 static void Main(string[] args) { Func(() => { return Task<int>.Run<int>(() => { return Enumerable.Range(1,100).Sum(); }); }); Thread.Sleep(5000);//等待異步執行完成 } static async void Func(Func<Task<int>> f) { int result = await f(); Console.WriteLine(result); } ---------------------------------