C# 5.0中引入了async 和 await。這兩個關鍵字可讓你更方便的按照同步的方式寫出異步代碼。也就是說使你更方便的異步編程。常規的寫法格式以下:web
- var result = await expression;
- statement(s);
這種寫法至關於: express
- 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);
- }
這是比較常見的寫法。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
因爲微軟並未給出知足上述條件的接口,所以能夠本身實現這樣的接口。
- 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);
- }
參考資料:《C# 5.0 IN A NUTSHELL》