iT邦幫忙

2025 iThome 鐵人賽

DAY 23
0

ValueTask 提高高同步命中率

public class CacheLookup
{
    private readonly Dictionary<string,int> _map = new();

    public ValueTask<int> GetAsync(string key)
    {
        if (_map.TryGetValue(key, out var v))
            return new ValueTask<int>(v);            // 同步快路徑,不配置 Task
        return new ValueTask<int>(SlowPathAsync(key)); // 退化為 Task
    }

    private async Task<int> SlowPathAsync(string key)
    {
        await Task.Delay(5); // 模擬 I/O
        int value = key.Length;
        _map[key] = value;
        return value;
    }
}

若約九成呼叫命中快取,可明顯降低配置與 GC;命中率偏低(例如四成以下)改用 Task 可讀性與維護性較佳。


自訂 IValueTaskSource

using System;
using System.Threading.Tasks.Sources;

sealed class PooledOp : IValueTaskSource<int>
{
    private ManualResetValueTaskSourceCore<int> _core;
    public short Version => _core.Version;

    public void Start()
    {
        // 啟動非同步作業(例如註冊 socket 回呼)
        // 完成時呼叫 SetResult 或 SetException
    }

    public void SetResult(int value) => _core.SetResult(value);
    public void SetException(Exception ex) => _core.SetException(ex);

    public int GetResult(short token) => _core.GetResult(token);
    public ValueTaskSourceStatus GetStatus(short token) => _core.GetStatus(token);
    public void OnCompleted(Action<object?> c, object? state, short token, ValueTaskSourceOnCompletedFlags flags)
        => _core.OnCompleted(c, state, token, flags);

    public ValueTask<int> AsValueTask() => new(this, Version);
    public void Reset() => _core.Reset(); // 歸還前重置
}

重點:物件歸還池前務必已完成並呼叫 Reset;未完成即重用會產生競態與錯誤結果。


診斷與調試

  • dotnet-counters monitor System.Runtime --counters ThreadPool.* 觀察佇列長度、執行緒數
  • dotnet-trace collect --providers Microsoft-DotNETRuntime:0x1C000080:5 追蹤 Task / ThreadPool 事件
  • VS Tasks 視圖:檢視未完成鏈與延續
  • 配置熱點:dotnet-gcdump + analyze
  • Starvation 指標:QueueLength 長期高且 worker 未跟上

快速檢查:

  1. 是否存在同步等待(Result / Wait)
  2. ASP.NET Core 中是否以 Task.Run 包裹純 I/O
  3. ValueTask 使用是否造成呼叫端誤判多次 await
  4. Library 熱路徑是否需要 ConfigureAwait(false) 降低上下文切換
- 核心意義
狀態機 MoveNext async 方法實際控制流程
Builder.SetResult / SetException 終結與填入結果
Continuation 等待完成後要執行的委派
TaskScheduler 決定延續執行位置與策略
ThreadPool 工作竊取 本地 LIFO,跨執行緒竊尾端減少爭用
ConfigureAwait(false) 不回傳 SynchronizationContext(保留 ExecutionContext)
ValueTask 高同步完成率、單次 await 場景減少配置
IValueTaskSource 可重用零額外配置非同步來源
UnsafeOnCompleted 不捕捉 ExecutionContext,僅限可信內部
Starvation ThreadPool 無足夠 worker 處理排隊工作
  1. async/await 生成狀態機與 awaiter 協定
  2. Task 是狀態與延續容器,完成後批次釋放延續
  3. ThreadPool 透過工作竊取與動態調整提供執行資源
  4. ValueTask 僅在高同步完成率且單次 await 有利
  5. ExecutionContext 捕捉具隱性成本,熱路徑需衡量
  6. 保持全鏈非同步並避免同步阻塞可減少 Starvation 與死鎖風險

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

尚未有邦友留言

立即登入留言