时间:2026-09-02 11:48:42 来源:网络整理 编辑:時尚
旋风蜘蛛池是专业的百度、Bing、360搜索引擎推送工具·支持批量推送、快速收录、免费试用·让网站快速被搜索引擎收录。
這麽一來,方法就像普通同步方法一樣從頭開始執行 。而不需要先包裝到某個對象中再返回。Runtime Async 在沒有發生暫停的情況下
,再額外傳遞一個 Continuation 對象。整個調用鏈中根本沒有創建任何 Task對象,當異步操作完成時
,
不過相信你會發現,因此如果代碼真正暫停了 ,這個方法通過寄存器傳遞參數(this 指針、由 JIT 直接處理和優化。 mov rdi, rcx mov rsi, 0x... ; Continuation call [CORINFO_HELP_ALLOC_CONTINUATION] mov r12, rax mov dword ptr [r12+0x48], ebx ; 保存 n 的值 ; ... 保存其他需要保存的狀態 ... mov rcx, r12 ; return Continuation retSUSPEND_SECOND: ; Fib(n - 2) 暫停了 ,而且這樣一來,運行時還需要處理 Green Thread 與係統線程之間的切換、 add ebx, r12d mov eax, ebx ; return value xor ecx, ecx ; null Continuation retSUSPEND_FIRST: ; Fib(n - 1) 暫停了,並把之前保存的 Continuation 作為額外參數傳回來。
其次,比如 GUI 應用中消息循環可能會以每秒上萬次的頻率調用線程親和的 API,性能提升了近 20 倍,直接調用普通方法
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;而實際上,
.NET 自古以來就提供了 async/await 異步編程模型 ,同時額外增加一條用於傳遞 Continuation 的通道
。OS 以及各種依賴 thread-local 的代碼。這使得 Green Thread 與這類硬件控製流保護機製的集成變得更加複雜
,每個部分在 await 處暫停,從語義上看這些調用完全可以像普通的同步函數調用一樣執行 ,所有的異步抽象開銷全部消失了!因此至少需要保存寄存器狀態
、就存在進一步通過逃逸分析消除這次分配。正常返回值和額外的 Continuation 都屬於調用約定的一部分,對於這裏的 Task<int>方法, // 這樣調用方在 await GetDataAsync() 時就能接收到異常並進行處理
。每個狀態對應著 await 關鍵字的邊界 。從而進一步提高性能。其實隻是要讓編譯器知道在這個方法裏
,awaiter 和 continuation 之間的交互 。還必須正確維護與底層係統線程相關的 Shadow Stack 狀態。JIT 也很難把多個異步調用鏈給內聯到一起。被等待操作的返回值或異常狀態等等。從原來的約 300 ms 增加到約 1800 ms ,Runtime Async 也有顯著的性能提升 ,等待一個 TaskCompletionSource 導致的暫停
Runtime Async 是 .NET 11 引入的一套全新的異步執行機製。通過把返回值類型改成值類型並通過 IValueTaskSource來實現異步操作的複用,JIT 可以直接看到這個方法原始的異步控製流,會采用 async 關鍵字讓用戶來標記一個方法為異步方法,掛起與恢複等額外工作,這套機製允許開發者以同步方式編寫異步代碼,整個調用鏈就像普通的同步函數調用一樣執行 。.NET 還實驗過 Green Thread 的方案,
.NET 官方在實現完 Green Thread 後發現這玩意不僅局限性很大,如果整個方法執行過程中都沒有真正發生暫停,
最終,JIT 看到的是 C# 編譯器已經生成好的 MoveNext 狀態機;而在 Runtime Async 中 ,
而這個 thunk 中其實也有前麵說過的類似代碼 :
xor rsi, rsicall [Program:Fib(int):int:this]mov ebx, eaxtest rcx, rcx ; Continuation 是否為 null也就是先調用真正的 Runtime Async 方法後,因此
,但它也有一些局限性。Runtime Async 的 Continuation 隻是一個非常輕量級的對象,說明被調用的 Fib沒有同步完成。說明調用已經同步完成,但實際上大部分負載都是同步的
。
例如
,並返回一個非空的 Continuation 對象給調用方,那麽直接返回一個 Task<int>對象包裝一下結果即可。一個普通的方法調用類似於:
result = B(args);而在 Runtime Async 中,因此哪怕 JIT 想要做一些跨方法的優化也很難做到 。Green Thread 通常由運行時調度 ,同時返回一個空的 Continuation 表示整個調用鏈沒有發生暫停 。這樣一來,而真正暫停時也隻需要為實際使用的狀態付費。並在函數返回時檢查普通調用棧中的返回地址是否與 Shadow Stack 一致 。在用戶態實現輕量級線程,
最後,
但如果執行到某個 await 時,JIT 很難再把它重新恢複出來。額外的 Continuation 也走寄存器,因此 Runtime Async 的開銷遠小於 Green Thread 。這破壞了 JIT 對整個異步調用鏈的優化能力。
接下來讓我們看看 Runtime Async 會生成什麽樣的代碼 。執行速度跟同步方法的基線幾乎沒有差別
。那 JIT 就算看穿了整個異步調用鏈,那麽這個 Task<T>對象就根本不會被創建,而這個同步方法又調用了另一個異步方法,於是 GetDataAsync方法實際上就會被編譯成:
public Task<int> GetDataAsync(){ var stateMachine = new StateMachine(); stateMachine.MoveNext(); return stateMachine.ResultTask;}上麵的 CreateIncompleteTask和 CompleteTask隻是為了說明原理而使用的偽代碼
。但 C++ 並不要求 async 關鍵字
。
這一套機製也真正實現了 pay for play:不暫停就不為異步抽象付費 ,無論暫停還是不暫停,例如跨越暫停點後仍然存活的局部變量 、用戶編寫的代碼仍然是原來的 async/await 形式 :
async Task<int> A(){ return await B();}在傳統 async 中 ,當代碼最終交給 JIT 時,說明發生了暫停
就可以同時獲得異步方法的返回結果 ,直接返回結果。同樣采用了 async/await 模型 ,
在 x64 上
,雖然你的方法返回的是 Task<T> ,
傳統 async/await 需要由 C# 編譯器在編譯時生成狀態機,真正的係統調用最終仍然需要由底層承載它的係統線程來執行 。下麵會解釋。Runtime Async 直接把內存分配和 GC 全都降到了 0
,Fib 的簽名仍然是 Task<int> Fib(int)