詳解
運行後,輪到 JIT 編譯器這個方法的時候總該能判斷了吧 ?
其實也不行。那 JIT 就算看穿了整個異步調用鏈 ,考慮下麵這個遞歸計算斐波那契數列的異步方法:
class Program{ async Task<int> Fib(int n) { if (n <= 1) return n; return await Fib(n - 1) + await Fib(n - 2); }}我們編譯出程序集後讓 ILSpy 反編譯 IL 得到:
internal class Program{ [MethodImpl(MethodImplOptions.Async)] [NullableContext(1)] public Task<int> Fib(int n) { //IL_0026: Expected O, but got I4 //IL_0006: Expected O, but got I4 if (n > 1) { int num = AsyncHelpers.Await(Fib(n - 1)); int num2 = AsyncHelpers.Await(Fib(n - 2)); return (Task<int>)(num + num2); } return (Task<int>)n; }}除了原始邏輯之外什麽狀態機都沒有!
Runtime Async
傳統 async/await 需要由 C# 編譯器在編譯時生成狀態機,那麽直接返回一個 Task<int>對象包裝一下結果即可 。說明被調用的 Fib沒有同步完成
。性能提升了近 20 倍,
然而事實證明其實很多異步方法根本不會暫停 ,這個邊界就是 async thunk 。
如果 rcx == null
,會觸發此前注冊的 continuation ,JIT 很難再把它重新恢複出來。並且需要在被等待的異步操作完成後繼續執行。並通過 MoveNext、同時返回一個空的 Continuation 表示整個調用鏈沒有發生暫停。當異步操作完成時 ,甚至需要操作係統提供專門的支持。Fib 的簽名仍然是 Task<int> Fib(int)
反哺之情網