熱點 | 成本來源 | 因應策略 |
---|---|---|
Task 分配 | GC 壓力 | 使用 ValueTask或重用 IValueTaskSource 來源 |
過多 context 捕捉 | ExecutionContext 複製 | 採用 UnsafeOnCompleted 內部機制或適度使用 SuppressFlow |
不必要呼叫 Task.Run | 額外排程開銷 | I/O 綁定操作直接採用原生非同步 API |
延續串過長 | 多次排程與堆疊消耗 | 採用扁平化設計,避免 return await 包裹純傳遞操作 |
阻塞呼叫(Result / Wait) | 線程集區耗盡 | 全鏈路改用非同步模式,必要時以 ValueTask.GetAwaiter().GetResult() 取代需確認已完成 |
微優化檢查清單:
反例 | 問題 | 修正方式 |
---|---|---|
公開 API 回傳 ValueTask 但呼叫次數少 | 複雜度高於節省的配置成本 | 改回傳回 Task |
多次等待同一 ValueTask | 行為未定義或觸發例外 | 先轉換為 AsTask() 再進行多次等待 |
lock 區塊內使用 await | 可能導致死鎖 | 改用非同步同步原語如 SemaphoreSlim |
使用 async void | 例外無法攔截處理 | 僅限事件處理器使用 |
Result 卡住 UI 或舊版 ASP.NET | 同步等待引發死鎖 | 全鏈路非同步化並搭配 ConfigureAwait(false) |
大量 Task.Run 包裹 I/O 操作 | 線程集區資源佔用 | 直接採用原生非同步 API |
using System;
using System.Threading;
using System.Threading.Tasks;
static async Task Demo()
{
Console.WriteLine($"A 執行緒={Environment.CurrentManagedThreadId}");
await Task.Delay(10); // 預設捕捉 SynchronizationContext(若存在)
Console.WriteLine($"B 執行緒={Environment.CurrentManagedThreadId}");
await Task.Delay(10).ConfigureAwait(false); // 不捕捉 SynchronizationContext
Console.WriteLine($"C 執行緒={Environment.CurrentManagedThreadId}");
}
await Demo();
無 UI SynchronizationContext Console 或 ASP.NET Core中,A/B/C 執行緒可能分散於線程集區或相同執行緒;WPF 環境下,B 會回傳至 UI 執行緒,C 則可能由線程集區執行。