You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在ASP.NET异步Controller中测量单个请求的CPU时间?

测量ASP.NET异步Controller中异步函数的CPU时间

针对异步场景下CPU时间统计的问题,这里提供几种可行的实现方案:

1. 手动拆分同步片段累计CPU时间

由于await前后代码可能切换线程,你可以在每个同步执行的代码块前后,记录当前线程的CPU时间并累加:

using System.Diagnostics;
using System.Threading;

public async Task<IActionResult> MyAsyncAction()
{
    long totalCpuTicks = 0;

    // 第一段同步逻辑
    var currentThread = Process.GetCurrentProcess().Threads.Cast<ProcessThread>()
        .First(t => t.Id == Thread.CurrentThread.ManagedThreadId);
    var startCpu = currentThread.TotalProcessorTime;
    // 执行同步操作
    SyncOperation1();
    var endCpu = currentThread.TotalProcessorTime;
    totalCpuTicks += (endCpu - startCpu).Ticks;

    await AsyncOperation1();

    // 第二段同步逻辑
    currentThread = Process.GetCurrentProcess().Threads.Cast<ProcessThread>()
        .First(t => t.Id == Thread.CurrentThread.ManagedThreadId);
    startCpu = currentThread.TotalProcessorTime;
    // 执行同步操作
    SyncOperation2();
    endCpu = currentThread.TotalProcessorTime;
    totalCpuTicks += (endCpu - startCpu).Ticks;

    var totalCpuTime = TimeSpan.FromTicks(totalCpuTicks);
    return Ok($"异步函数总CPU时间:{totalCpuTime.TotalMilliseconds}ms");
}

这种方式需要拆分异步代码中的同步片段,适合对代码有完全控制权的场景,能精准统计各执行段的CPU消耗。

2. 结合Activity追踪异步执行路径

利用.NET的Activity追踪异步操作的生命周期,配合自定义事件监听来收集CPU时间:

using System.Diagnostics;

public async Task<IActionResult> MyAsyncAction()
{
    using var activity = new Activity("AsyncActionTrace").Start();
    // 注册事件监听(可在全局初始化)
    var listener = new ActivityListener
    {
        ShouldListenTo = _ => true,
        ActivityStarted = activity =>
        {
            // 记录活动开始时当前线程的CPU时间
            var thread = Process.GetCurrentProcess().Threads.Cast<ProcessThread>()
                .First(t => t.Id == Thread.CurrentThread.ManagedThreadId);
            activity.SetTag("StartCpuTicks", thread.TotalProcessorTime.Ticks);
        },
        ActivityStopped = activity =>
        {
            // 计算活动结束时的CPU时间差并累加
            var thread = Process.GetCurrentProcess().Threads.Cast<ProcessThread>()
                .First(t => t.Id == Thread.CurrentThread.ManagedThreadId);
            var startTicks = activity.GetTagItem("StartCpuTicks") as long? ?? 0;
            var cpuTicksUsed = thread.TotalProcessorTime.Ticks - startTicks;
            // 将结果存入活动或自定义存储
            activity.SetTag("CpuTicksUsed", cpuTicksUsed);
        }
    };
    ActivitySource.AddActivityListener(listener);

    // 执行异步操作
    await AsyncOperation1();
    await AsyncOperation2();

    var totalCpu = TimeSpan.FromTicks((long)activity.GetTagItem("CpuTicksUsed"));
    return Ok($"总CPU时间:{totalCpu.TotalMilliseconds}ms");
}

这种方案能自动追踪异步操作的各个阶段,无需手动拆分代码,适合复杂异步流程的统计。

3. 性能计数器近似统计

通过PerformanceCounter对进程CPU使用率进行采样,得到异步操作期间的整体CPU消耗情况:

using System.Diagnostics;

public async Task<IActionResult> MyAsyncAction()
{
    bool isCompleted = false;
    long totalCpuValue = 0;
    int sampleCount = 0;

    // 启动后台采样任务
    var samplingTask = Task.Run(async () =>
    {
        var cpuCounter = new PerformanceCounter(
            "Process", "% Processor Time", Process.GetCurrentProcess().ProcessName);
        // 第一次采样需跳过
        cpuCounter.NextValue();
        while (!isCompleted)
        {
            totalCpuValue += (long)cpuCounter.NextValue();
            sampleCount++;
            await Task.Delay(10);
        }
    });

    // 执行目标异步操作
    await AsyncOperation1();
    await AsyncOperation2();

    isCompleted = true;
    await samplingTask;

    var averageCpuUsage = totalCpuValue / sampleCount;
    return Ok($"异步操作期间平均CPU使用率:{averageCpuUsage}%");
}

这种方法属于近似统计,适合不需要精确到单个线程片段的场景,能快速获取整体CPU负载情况。

以上方案都无法做到绝对精确(线程池线程可能被其他请求复用),但足以满足大多数性能分析需求。

内容的提问来源于stack exchange,提问作者skyde

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 21:20:43