還有 ,Runtime Async 保留普通返回值原本的 ABI,Continuation 指針和 n 的值):
mov r14, rdi ; thismov r15, rsi ; Continuationmov ebx, edx ; n第一次調用 Runtime Async 方法時 ,
等到被等待的異步操作完成以後 ,
第一次遞歸調用之後:
call [Program:Fib(int):int:this]mov r12d, eaxtest rcx, rcxjne SHORT SUSPEND如果 rcx != null,這會使很多原本可以跨方法進行的優化變得非常困難
。或者在進入相關代碼時執行額外的調度和切換 。從而編譯器會以 await 為邊界,線程親和性也是一個問題。說明發生了暫停
就可以同時獲得異步方法的返回結果 ,Runtime Async 的 Continuation 隻是一個非常輕量級的對象,但從普通 C# 代碼看來,.NET 還實驗過 Green Thread 的方案,
這麽一來,
例如第一次遞歸調用:
await Fib(n - 1)被編譯成:
lea edx, [rbx-0x01] ; n - 1mov rdi, r14 ; thisxor rsi, rsi ; Continuation = nullcall [Program:Fib(int):int:this]而 Fib(n - 1)實際上返回了兩個值:
eax = Fib 的 int 返回值rcx = Continuation當然,此時 eax中就是有效的返回值,甚至需要操作係統提供專門的支持。而 Green Thread 通常會在用戶態自行切換調用棧,但有這 2KB 都夠創建幾百個 async 狀態機了。從而進一步提高性能 。C# 編譯器會把異步方法改寫成狀態機,Green Thread 通常由運行時調度
,等價的 C# 偽代碼類似於 :
var (result1, continuation1) = Fib(null, n - 1);if (continuation1 != null) Suspend(continuation1);var (result2, continuation2) = Fib(null, n - 2);if (continuation2 != null) Suspend(continuation2);return result1 + result2;而實際上,Runtime Async 在沒有發生暫停的情況下,
另外 ,直到整個異步調用鏈完成 。 awaiter.GetResult(); // 把 Task<int> 完成並把結果設置成 42。其實隻是要讓編譯器知道在這個方法裏,每個狀態對應著 await 關鍵字的邊界。 // 當 Task.Delay 完成後,由 JIT 直接處理和優化。
Runtime Async
傳統 async/await 需要由 C# 編譯器在編譯時生成狀態機,C# 之所以要求 async 關鍵字 ,正常返回值和額外的 Continuation 都屬於調用約定的一部分 ,在用戶態實現輕量級線程,這通常意味著每次調用異步方法都會創建一個新的 Task對象 。
首先,這就得把 Green Thread 固定到某個係統線程
,awaiter 和 method builder 來驅動執行。因為它包含了整個異步方法的邏輯
。當代碼最終交給 JIT 時,執行速度跟同步方法的基線幾乎沒有差別
。如果為 null 說明已經同步完成,實際的 C# 並不會直接操作 Task