iT邦幫忙

2025 iThome 鐵人賽

DAY 25
0

以下是流程簡單的描述

  1. 初始:已建立/等待啟動
  2. 呼叫 Start 或被排程 → 進入等待執行
  3. 執行緒池呼叫 ExecuteEntry → 執行中(委派已觸發)
  4. 使用者委派執行結果:成功/拋出例外/拋出 OperationCanceledException
  5. 標記使用者委派已執行
  6. 若有子任務則等待全部完成 → 子任務完成
  7. 第二階段收斂:設定狀態 RanToCompletion/Faulted/Canceled
  8. 第三階段:清理資源+通知父任務+排程接續
  9. 後續觸發:await/ContinueWith/WhenAll 等

IL 對應 await

IL_000a: callvirt  instance class [System.Net.Http]System.Threading.Tasks.Task`1<string>
                         [System.Net.Http]System.Net.Http.HttpClient::GetStringAsync(string)
IL_000f: callvirt  instance valuetype [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<string>
                         class [System.Runtime]System.Threading.Tasks.Task`1<string>::GetAwaiter()
IL_0014: stloc.0                      // 暫存 awaiter
IL_0015: ldloca.s 0
IL_0017: call instance bool [System.Runtime]System.Runtime.CompilerServices.TaskAwaiter`1<string>::get_IsCompleted()
IL_001c: brtrue.s   IL_0039
// 未完成路徑(掛起)
IL_001e: ldarg.0
IL_001f: ldc.i4.0
IL_0020: stfld int32 <>1__state
IL_0025: ldarg.0
IL_0026: ldloc.0
IL_0027: stfld valuetype TaskAwaiter`1<string> <>u__1
IL_002c: ldarg.0
IL_002d: ldflda valuetype AsyncTaskMethodBuilder`1<int32> <>t__builder
IL_0032: ldloca.s 0
IL_0034: ldarg.0
IL_0035: call instance void AsyncTaskMethodBuilder`1<int32>::AwaitUnsafeOnCompleted(
                     !!0&, !!1&)
IL_003a: ret
// 已完成快速路徑
IL_0039: ldloca.s 0
IL_003b: call instance string TaskAwaiter`1<string>::GetResult()

Awaiter 基本契約

可被 await 的型別需提供:

  1. GetAwaiter()
  2. Awaiter 需具備:bool IsCompleted、void OnCompleted(Action) 或 UnsafeOnCompleted(Action)、TResult GetResult()

TaskAwaiter / ValueTaskAwaiter 為內建。AwaitUnsafeOnCompleted 優先使用 UnsafeOnCompleted 以避免不必要的 ExecutionContext 複製。

同步快速路徑

IsCompleted 為 true 時直接續執行,不掛起也不排程。大量同步命中(快取、已完成 I/O)是使用 ValueTask 降低配置的前提。

例外與取消

  • 例外於 MoveNext 中被捕捉並呼叫 SetException,狀態為 Faulted
  • OperationCanceledException(含符合 Token)最終標記為 Canceled
  • await 的 GetResult 重新拋出第一層原始例外(非 Aggregate 包)

ConfigureAwait(false)

影響續接排程:

  • 預設 true:捕捉 SynchronizationContext(如 UI)
  • false:略過 SynchronizationContext,但仍流動 ExecutionContext
  • IL 會使用 ConfiguredTaskAwaitable.ConfiguredTaskAwaiter,狀態機模式不變

Q. ValueTask 差異
AsyncValueTaskMethodBuilder:

  • 同步完成:直接包裝結果,不配置 Task
  • 真正非同步:可能配置 Task 或使用 IValueTaskSource
  • 同一個 ValueTask 不應多次 await(除非轉為 AsTask)

Q. 為何可能出現盒裝狀態機
ref struct 無法實作介面,當需以 IAsyncStateMachine 形式傳遞(如偵錯掛勾)可能產生一次性 boxing,編譯器已盡量避免。

常見效能觀察

現象 原因 對策
小型高頻 async 造成大量 GC 每次非同步配置 Task 使用 ValueTask(同步命中高)或合併 await
深層 await 堆疊大 多層 return await 僅傳遞結果 直接 return Task
UI 被阻塞 使用 .Result 等同步等待 全鏈 async,函式庫側可加 ConfigureAwait(false)
傳統環境死鎖 同步等待造成上下文阻塞 避免同步等待或使用 ConfigureAwait(false)

主要概念

  • 快速路徑:IsCompleted 即續執行
  • 真正掛起點:AwaitUnsafeOnCompleted 註冊續接
  • 完成後三步:狀態設定 → 清理與通知 → 續接執行
  • 效能主軸:減少不必要 Task 配置、降低堆疊、避免同步阻塞

上一篇
async/await 降糖展開(狀態機 IL 分析)
系列文
新 .NET & Azure & IoT & AI 開源技術實戰手冊 (含深入官方程式碼講解) 25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言