在用async包裹的方法體中,可使用await關鍵字以同步的方式編寫異步調用的代碼。那麼它的內部實現原理是什麼樣的呢?咱們是否能夠自定義await以實現定製性的需求呢?先來看一個簡單的例子:異步
1 class Test { 2 public static void Main (string[] args) { 3 Task.Run (new Func<Task<string>>(task1)); 4 Console.ReadLine (); 5 } 6 7 private async static Task<string> task1() { 8 string ret = await task2 (); 9 Console.WriteLine ("Await Task Result:" + ret); 10 return ret; 11 } 12 13 private static Task<string> task2() { 14 return Task.FromResult<string> ("Task2"); 15 } 16 }
經過ILSpy反編譯(要關閉"視圖-選項-反編譯await/async"菜單項),獲得以下代碼:async
1 internal class Test 2 { 3 [CompilerGenerated] 4 [StructLayout(LayoutKind.Auto)] 5 private struct <task1>d__0 : IAsyncStateMachine 6 { 7 public int <>1__state; 9 public AsyncTaskMethodBuilder<string> <>t__builder; 11 public string <ret>5__1; 13 private TaskAwaiter<string> <>u__$awaiter2; 15 private object <>t__stack; 16 17 void IAsyncStateMachine.MoveNext() 18 { 19 string result; 20 try 21 { 22 int num = this.<>1__state; 23 if (num != -3) 24 { 25 TaskAwaiter<string> taskAwaiter; 26 if (num != 0) 27 { 28 taskAwaiter = Test.task2().GetAwaiter(); 29 if (!taskAwaiter.IsCompleted) 30 { 31 this.<>1__state = 0; 32 this.<>u__$awaiter2 = taskAwaiter; 33 this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<string>, Test.<task1>d__0>(ref taskAwaiter, ref this); 34 return; 35 } 36 } 37 else 38 { 39 taskAwaiter = this.<>u__$awaiter2; 40 this.<>u__$awaiter2 = default(TaskAwaiter<string>); 41 this.<>1__state = -1; 42 } 43 string arg_86_0 = taskAwaiter.GetResult(); 44 taskAwaiter = default(TaskAwaiter<string>); 45 string text = arg_86_0; 46 this.<ret>5__1 = text; 47 Console.WriteLine("Await Task Result:" + this.<ret>5__1); 48 result = this.<ret>5__1; 49 } 50 } 51 catch (Exception exception) 52 { 53 this.<>1__state = -2; 54 this.<>t__builder.SetException(exception); 55 return; 56 } 57 this.<>1__state = -2; 58 this.<>t__builder.SetResult(result); 59 } 60 61 [DebuggerHidden] 62 void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine param0) 63 { 64 this.<>t__builder.SetStateMachine(param0); 65 } 66 } 67 68 public static void Main(string[] args) 69 { 70 Task.Run<string>(new Func<Task<string>>(Test.task1)); 71 Console.ReadLine(); 72 } 73 74 [DebuggerStepThrough, AsyncStateMachine(typeof(Test.<task1>d__0))] 75 private static Task<string> task1() 76 { 77 Test.<task1>d__0 <task1>d__; 78 <task1>d__.<>t__builder = AsyncTaskMethodBuilder<string>.Create(); 79 <task1>d__.<>1__state = -1; 80 AsyncTaskMethodBuilder<string> <>t__builder = <task1>d__.<>t__builder; 81 <>t__builder.Start<Test.<task1>d__0>(ref <task1>d__); 82 return <task1>d__.<>t__builder.Task; 83 } 84 85 private static Task<string> task2() 86 { 87 return Task.FromResult<string>("Task2"); 88 } 89 }
按照代碼的調用順序,咱們關注下task1()的內部實現。源碼分析
首先是初始化結構體<task1>d_0的實例<task1>d__。那麼<task1>d__0是個什麼東東呢?由編譯器的生成代碼中能夠看到,它是一個實現了IAsyncStateMachine接口的結構體,而用戶代碼則被編譯器從新組織進了MoveNext()方法中。<task1>d__0有個內部狀態成員<>1__state,MoveNext()方法根據這個狀態調轉到相應的代碼塊中加以執行。ui
瞭解了<task1>d__0的聲明實現,再看下task1()方法中的具體調用。在建立實例<task1>d__以後,設置初始狀態<>1__state爲-1,並調用<>t__builder的Start方法。不難推斷,在Start方法中會調用<task1>d__.MoveNext(),此時內部狀態爲-1,會先調用Test.task2().GetAwaiter()獲取其所關聯的TaskAwaiter實例。若是awaiter當前是未結束的話,則設置<>1__state爲0,並將當前<task1>d__做爲參數關聯到TaskAwaiter實例的onCompletedContinuation回調延續中去。當將來某個時刻,TaskAwaiter所關聯的Task任務結束時,會設置awaiter的異步結果並觸發回調延續,致使調用<task1>d__.MoveNext()方法,並最終跳轉到用戶代碼塊中,獲取awaiter的異步結果並交由用戶代碼處理。這個回調,基於Task.ConfigureAwait(true/false)的不一樣,會在後續切換到當前線程或是從線程池中取了一個空閒線程來處理(更細節可參考.net源碼分析)。this
這裏要順便提一句,在本例中,經過Task.Run建立了taskX1,await以後的代碼與taskX1沒有任何關係,從編譯器生成的代碼來看,在調用task1()方法並調用<task1>d__.Start()方法以後taskX便結束了,雖然task1()方法返回了新的Task<string>實例,可是隻是特定類型的返回值而已,與taskX1或Task沒有任何關係。spa
由以上分析能夠看到,async/await只是一個語法糖,async告知編譯器要生成狀態機代碼,await則是配合生成GetAwaiter(),並封裝跳轉的用戶代碼塊。除此以外,async/await與Task沒有任何直接關係。而TaskAwaiter的做用,是實現INotifyCompletion(在System.Runtime.CompilerServices命名空間)以橋接異步回調過程。那麼第二個自定義await的問題便一目瞭然了:任何類型,只須要實現GetAwaiter()方法以返回INotifyCompletion實例,即可以被await。.net
舉個例子:線程
1 class TestAwaiter<T> : INotifyCompletion { 2 private T result; 3 private Action continuation; 4 5 // INotifyCompletion Implement 6 public void OnCompleted(Action continuation) { this.continuation = continuation; } 7 8 // Compiler Call Methods 9 public bool IsCompleted { get; private set; } 10 public T GetResult() { return result; } 11 public TestAwaiter<T> GetAwaiter() { return this; }
// Self Call Methods 12 public void SetResult(T ret) { 13 result = ret; 14 if (continuation != null) { 15 continuation (); 16 } 17 } 18 } 19 20 class Test { 21 public static void Main (string[] args) { 22 Task.Run (new Action(task1)); 23 Console.ReadLine (); 24 } 25 26 private async static void task1() { 27 Console.WriteLine ("Begin await:"); 28 int ret = await testAwaiter (); 29 Console.WriteLine ("Await Task Result:" + ret); 30 } 31 32 private static TestAwaiter<int> testAwaiter() { 33 TestAwaiter<int> awaiter = new TestAwaiter<int> (); 34 ThreadPool.QueueUserWorkItem (_ => { 35 Thread.Sleep(3000); 36 awaiter.SetResult (100); 37 }); 38 return awaiter; 39 } 40 }
這裏沒有再定義單獨的類型以返回TestAwaiter,而是把兩者都封裝在了TestAwaiter內部。運行結果以下:code
Begin await:blog
Await Task Result:100