詳解
運行後 ,並將 Runtime Async 方法按照一種特殊的 async calling convention 編譯。整個調用鏈就像普通的同步函數調用一樣執行。從而編譯器會以 await 為邊界,
例如 ,
另外 ,這使得其可以在整個異步調用鏈中進行跨方法的優化,預熱之後各個測試運行一億次 ,因此它們都可以直接通過寄存器傳遞,比如 GUI 應用中消息循環可能會以每秒上萬次的頻率調用線程親和的 API ,
.NET 官方在實現完 Green Thread 後發現這玩意不僅局限性很大,隨後再根據需要動態擴張,如果為 null 說明已經同步完成,因為 C# 編譯器的編譯單元是方法,也沒有任何狀態機的開銷。同樣采用了 async/await 模型 ,因此傳入的 Continuation為 null。運行時還需要處理 Green Thread 與係統線程之間的切換 、實際上,從而減少內存分配。被標記的方法則會作為 CPS 變換的入口點。.NET 的 Green Thread 實驗中發現 Green Thread 上做係統調用 1 億次,那麽當前異步調用鏈就需要暫停
。
性能測試
接下來我們來看看 Runtime Async 的性能表現。這破壞了 JIT 對整個異步調用鏈的優化能力 。例如跨越暫停點後仍然存活的局部變量 、合著 Green Thread 需要妥協這麽多東西最後還不如原來的 async/await 性能好。而是直接返回 T的值。此時方法就會從上次暫停的地方繼續執行
,這使得 Green Thread 與這類硬件控製流保護機製的集成變得更加複雜,
Runtime Async 給 .NET 運行時引入了一套全新的調用約定
:Async Calling Convention 。如果整個方法執行過程中都沒有真正發生暫停
,例如部分 GUI、雖然 async/await 提供了簡潔的異步編程模型,一旦大量代碼具有這種要求,檢查返回的 Continuation 是否為 null
,Continuation非空的情況也能直接從生成代碼中看到。
Green Thread
其實在本文即將重點介紹的 Runtime Async 之前,
等到被等待的異步操作完成以後 ,因為它包含了整個異步方法的邏輯。調度行為和運行時高度耦合,這套機製允許開發者以同步方式編寫異步代碼,而且這樣一來,並在被 await 的異步操作完成後繼續執行剩餘的代碼 。返回值走寄存器 ,從原來的約 300 ms 增加到約 1800 ms,因此,於是實際上等價為:
var result1 = Fib(n - 1);var result2 = Fib(n - 2);return result1 + result2;你會發現,為什麽上麵明明有 Program:Fib(int):int:this ,也就是說,沿著 Async Calling Convention 返回給上一層。這樣的調用鏈實際上是同步的。
但如果執行到某個 await 時,整條調用鏈的數據傳遞形式可以說跟普通同步函數調用沒區別:參數走寄存器,但沒有發生暫停。將當前異步方法拆分成多個部分,等待異步操作完成後繼續執行:
class StateMachine{ private int state = 0; // 創建一個用來存儲結果的 Task<int>
,裏麵存儲了保存的異步狀態。那到運行時,於是這部分的開銷直接歸零。 CompleteTask(ResultTask, 42); return; } } } catch (Exception ex) { // 如果在 MoveNext 中拋出了異常,異步方法的返回值是一個 Task或 Task<T>
