異步函數簡介異步
通常指 async 修飾符聲明得、可包含await表達式得方法或匿名函數。async
聲明方式ide
異步方法的聲明語法與其餘方法徹底同樣, 只是須要包含 async 關鍵字。async能夠出如今返回值以前的任何位置, 以下示例:函數
async public static void GetInfoAsync() { //... } public async static void GetInfoAsync() { //... } public static async void GetInfoAsync() { //... }
異步方法的返回類型工具
異步函數的返回類型只能爲: void、Task、Task<TResult>。ui
Task<TResult>: 表明一個返回值T類型的操做。this
Task: 表明一個無返回值的操做。spa
void: 爲了和傳統的事件處理程序兼容而設計。設計
await(等待)3d
await等待的是什麼? 能夠是一個異步操做(Task)、亦或者是具有返回值的異步操做(Task<TResult>)的值, 以下:
public async static void GetInfoAsync() { await GetData(); // 等待異步操做, 無返回值 await GetData<int>(1); //等待異步操做, 返回值 int } static Task GetData() { //... return null; } static Task<T> GetData<T>(int a) { //... return null; }
注: await 最終操做的是一個值, 固然, 也能夠是無值, 如上GetData() , 不然就是一個 Task<T> 如上: GetData<T>()
await執行過程
TaskAwaiter 獲取執行結果
通常而言, await等待的一個異步操做, 不管是具有返回值仍是否, 那麼最終都會得到該操做是否已完成、具有返回值得異步操做能夠獲取他得返回結果。
因此這個時候, TaskAwaiter出現了, 不管是Task、仍是Task<TResult>操做, 都具有GetAwaiter() 方法。
用於獲取改操做得狀態、返回結果, 及部分操做, 以下TaskAwaiter結構:
// // 摘要: // 提供等待異步任務完成的對象。 public struct TaskAwaiter : ICriticalNotifyCompletion, INotifyCompletion { // // 摘要: // 獲取一個值,該值指示是否已完成的異步任務。 // // 返回結果: // true 若是任務已完成;不然爲 false。 // // 異常: // T:System.NullReferenceException: // System.Runtime.CompilerServices.TaskAwaiter 對象未正確初始化。 public bool IsCompleted { get; } // // 摘要: // 結束異步任務完成以前的等待。 // // 異常: // T:System.NullReferenceException: // System.Runtime.CompilerServices.TaskAwaiter 對象未正確初始化。 // // T:System.Threading.Tasks.TaskCanceledException: // 任務已取消。 // // T:System.Exception: // 在完成的任務 System.Threading.Tasks.TaskStatus.Faulted 狀態。 [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries")] public void GetResult(); // // 摘要: // 設置時應執行的操做 System.Runtime.CompilerServices.TaskAwaiter 對象中止等待異步任務完成。 // // 參數: // continuation: // 要在等待操做完成時執行的操做。 // // 異常: // T:System.ArgumentNullException: // continuation 爲 null。 // // T:System.NullReferenceException: // System.Runtime.CompilerServices.TaskAwaiter 對象未正確初始化。 [SecuritySafeCritical] public void OnCompleted(Action continuation); // // 摘要: // 計劃程序與此等待異步任務的延續任務操做。 // // 參數: // continuation: // 要等待操做完成時調用的操做。 // // 異常: // T:System.ArgumentNullException: // continuation 爲 null。 // // T:System.InvalidOperationException: // 該等待程序未正確初始化。 [SecurityCritical] public void UnsafeOnCompleted(Action continuation); }
接下來, 演示如何經過等待去獲取異步操做的返回結果, 以下代碼所示:
public async static void GetInfoAsync() { Task<bool> task = Task.Run<bool>(() => { Thread.Sleep(10000); //模擬耗時 return true; }); //如下兩種方式 bool taskResult1 = await task; //內部本身執行了GetAwaiter() bool taskResult = task.GetAwaiter().GetResult(); //本身手動執行Awaiter(), 可是阻塞UI
Console.WriteLine(taskResult); }
注: 對於一個await表達式, 編譯器生成的代碼會先調用GetAwaiter(), 而後經過awaiter得成員來等待結果, 因此以上兩種方式等效( 不考慮阻塞的狀況下)
爲了驗證以上猜想, 經過反編譯工具查看獲得以下代碼:
編譯器最終生成兩個密封類, 一個類( <>c )咱們展開分析:
<GetInfoAsync>b__1_0() 正是模擬耗時的一個操做委託生成的方法。
[CompilerGenerated] [Serializable] private sealed class <>c { public static readonly Program.<>c <>9 = new Program.<>c(); public static Func<bool> <>9__1_0; internal bool <GetInfoAsync>b__1_0() { Thread.Sleep(10000); return true; } }
第二個類 <GetInfoAsync>d__1 分析:
該類分別實現了接口 IAsyncStateMachine 的MoveNext() 與 SetStateMachine() ,另外 注意,
還特別定義了一個 <>t__builder, 先記住他, 下面講會對他講到, 爲何編譯器生成的代碼會默認先調用GetAwaiter()
1 [CompilerGenerated] 2 private sealed class <GetInfoAsync>d__1 : IAsyncStateMachine 3 { 4 public int <>1__state; 5 public AsyncVoidMethodBuilder <>t__builder; 6 private Task<bool> <task>5__1; 7 private bool <result>5__2; 8 private bool <>s__3; 9 private TaskAwaiter<bool> <>u__1; 10 void IAsyncStateMachine.MoveNext() 11 { 12 int num = this.<>1__state; 13 try 14 { 15 TaskAwaiter<bool> awaiter; 16 if (num != 0) 17 { 18 Func<bool> arg_2F_0; 19 if ((arg_2F_0 = Program.<>c.<>9__1_0) == null) 20 { 21 arg_2F_0 = (Program.<>c.<>9__1_0 = new Func<bool>(Program.<>c.<>9.<GetInfoAsync>b__1_0)); 22 } 23 this.<task>5__1 = Task.Run<bool>(arg_2F_0); 24 awaiter = this.<task>5__1.GetAwaiter(); 25 if (!awaiter.IsCompleted) 26 { 27 this.<>1__state = 0; 28 this.<>u__1 = awaiter; 29 Program.<GetInfoAsync>d__1 <GetInfoAsync>d__ = this; 30 this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<bool>, Program.<GetInfoAsync>d__1>(ref awaiter, ref <GetInfoAsync>d__); 31 return; 32 } 33 } 34 else 35 { 36 awaiter = this.<>u__1; 37 this.<>u__1 = default(TaskAwaiter<bool>); 38 this.<>1__state = -1; 39 } 40 this.<>s__3 = awaiter.GetResult(); 41 this.<result>5__2 = this.<>s__3; 42 Console.WriteLine(this.<result>5__2); 43 } 44 catch (Exception exception) 45 { 46 this.<>1__state = -2; 47 this.<>t__builder.SetException(exception); 48 return; 49 } 50 this.<>1__state = -2; 51 this.<>t__builder.SetResult(); 52 } 53 [DebuggerHidden] 54 void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) 55 { 56 } 57 }
接下來, 看GetInfoAsync()方法, 這個是本身編寫的, 可是實現的細節,最終轉換成了編譯器執行代碼:
[AsyncStateMachine(typeof(Program.<GetInfoAsync>d__1)), DebuggerStepThrough] public static void GetInfoAsync() { Program.<GetInfoAsync>d__1 <GetInfoAsync>d__ = new Program.<GetInfoAsync>d__1(); <GetInfoAsync>d__.<>t__builder = AsyncVoidMethodBuilder.Create(); <GetInfoAsync>d__.<>1__state = -1; AsyncVoidMethodBuilder <>t__builder = <GetInfoAsync>d__.<>t__builder; <>t__builder.Start<Program.<GetInfoAsync>d__1>(ref <GetInfoAsync>d__); //注意到該代碼, 調用了Start(),也許這就是默認實現的地方 }
經過查看Start泛型方法的實現, 最終找到了, 該泛型的條件限制於必須實現與 IAsyncStateMachine 接口, 因此經過查看, 該類最終調用了 MoveNext(), 而MoveNext中正
調用了GetAwaiter()。關於Start的實現以下所示:
[SecuritySafeCritical, DebuggerStepThrough, __DynamicallyInvokable] public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { if (stateMachine == null) { throw new ArgumentNullException("stateMachine"); } ExecutionContextSwitcher executionContextSwitcher = default(ExecutionContextSwitcher); RuntimeHelpers.PrepareConstrainedRegions(); try { ExecutionContext.EstablishCopyOnWriteScope(ref executionContextSwitcher); stateMachine.MoveNext(); } finally { executionContextSwitcher.Undo(); } }
剖析MoveNext
對比IDE中的代碼, 以下所示:
總結
await等待的是任務的操做值, 最終返回是異步操做的返回結果。而這一切都是由於編譯器建立了一系列複雜的狀態機制, 以達到其實現。