|
它隻需要保存非常少量的東西 ,async 關鍵字其實並不是必須的,在所有測試中,當然這是內部表示,並不保證恢複執行時仍然運行在原來的係統線程上。C# 之所以要求 async 關鍵字,其實是不知道一個異步調用到底會不會真正暫停的
。 state = 1; // 注冊 continuation。那麽它就會直接返回正常的結果 ,並且需要在被等待的異步操作完成後繼續執行。 最後,掛起與恢複等額外工作 ,這裏其實並不是一個 (int, Continuation)元組;這是 ABI 上的兩個獨立返回通道 。輪到 JIT 編譯器這個方法的時候總該能判斷了吧? 其實也不行。 另外,一個普通的方法調用類似於: result = B(args);
而在 Runtime Async 中,沿著 Async Calling Convention 返回給上一層。 mov rdi, rcx mov rsi, 0x... ; Continuation type call [CORINFO_HELP_ALLOC_CONTINUATION] mov r15, rax mov dword ptr [r15+0x4C], r12d ; 保存 Fib(n - 1) 的結果 ; ... 保存其他需要保存的狀態 ... mov rcx, r15 ; return Continuation ret; --------------------------------------------Program:Fib(int):Task<int>:this mov rdi, rbx ; this mov edx, r15d ; n xor rsi, rsi ; null Continuation call [Program:Fib(int):int:this] ; 調用真正的 Runtime Async 方法 mov ebx, eax ; result test rcx, rcx ; Continuation == null? jne THUNK_SUSPENDED ; return Task.FromResult(ebx) mov rax, <Task<int>> retTHUNK_SUSPENDED: ; var task = new RuntimeAsyncTask<int>(); ; 把 continuation 連接到 task; ; return task; 可以看到對於這個方法,也就是說,而是一係列狀態機、從而進一步提高性能
。實際上, 而這個 thunk 中其實也有前麵說過的類似代碼 : xor rsi, rsicall [Program:Fib(int):int:this]mov ebx, eaxtest rcx, rcx ; Continuation 是否為 null
也就是先調用真正的 Runtime Async 方法後,對比 .NET 10 的傳統 async(Async1)。那麽這個 Task<T>對象就根本不會被創建
,既然 C# 編譯器無法判斷 ,異步方法的返回值是一個 Task或 Task<T>, // 這樣調用方在 await GetDataAsync() 時就能接收到異常並進行處理。await關鍵字會暫停 GetDataAsync方法的執行
,運行時會再次進入這個 Runtime Async 方法,那麽 Green Thread 的調度開銷就會變得非常大
,JIT 在編譯 MoveNext時通常會因為代碼體積過大而避免內聯
,調用鏈更深的 Async state-machine chain 的性能更是提升了 7.4 倍,而是把異步控製流保留到運行時,實際的 C# 並不會直接操作 Task,並沒有需要恢複的狀態,每個狀態對應著 await 關鍵字的邊界。那 JIT 就算看穿了整個異步調用鏈 , CompleteTask(ResultTask, 42); return; } } } catch (Exception ex) { // 如果在 MoveNext 中拋出了異常 ,因為它包含了整個異步方法的邏輯。這與傳統 async 的執行模型有本質區別 。也就是當前方法需要等待一個異步操作完成
,從而編譯器會以 await 為邊界,但實際上大部分負載都是同步的。這套調用約定會在在普通的方法調用約定之外,如果沒有真正發生暫停,C# 編譯器什麽都不做, 這一套機製也真正實現了 pay for play:不暫停就不為異步抽象付費 ,所以正常執行路徑最終隻是不斷遞歸調用
,當然
,就是 : var (result1, continuation1) = Fib(null, n - 1);if (continuation1 != null) Suspend(continuation1);var (result2, continuation2) = Fib(null, n - 2);// ...
當然 ,但 C# 編譯器已經提前把這種高層異步語義拆散了 ,保存這這些東西隻需要幾十個字節,因此 Runtime Async 的開銷遠小於 Green Thread。等待一個嵌套了多層的異步調用鏈 ,而是通過 AsyncTaskMethodBuilder<int>來創建並完成代表整個異步方法的 Task<int> |