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

C#跨多线程统计已流逝挂钟时间的最佳实现方案是什么?

你担心的Stopwatch非线程安全问题,本质是指并发调用修改类方法(Start/Stop/Reset/Restart)、或者同时读写属性时会出现异常。如果你的使用逻辑是仅在主线程启动一次Stopwatch,工作线程全程只读取ElapsedMilliseconds这类只读属性,主线程直到所有工作线程退出后才调用Stop方法,那这种场景下直接用Stopwatch是完全安全的,不会出现线程冲突。

如果想要更稳妥、完全不需要考虑线程安全的方案,可以直接用Stopwatch的静态方法记录时间戳,不需要共享Stopwatch实例:
Stopwatch.GetTimestamp()是线程安全的静态方法,多线程同时调用不会有任何问题。你只需要在主线程启动任务前调用该方法记录起始滴答数,存为只读变量,后续任意工作线程需要计算流逝时间时,再次调用Stopwatch.GetTimestamp()获取当前滴答数,和起始值做差后按频率换算为毫秒即可,换算公式为:
流逝毫秒数 = (当前滴答数 - 起始滴答数) * 1000.0 / Stopwatch.Frequency

优化后的参考代码如下:

long startTimeStamp;
CountdownEvent pendingJobs = new CountdownEvent(0);
// 建议使用线程安全队列,避免多线程出队异常
ConcurrentQueue<YourJobType> workQueue = new ConcurrentQueue<YourJobType>();
const int TimeoutMilliseconds = 5000;

void SpawnProcessors(int chosenNumberOfThreads) 
{
    // 记录起始时间戳,无线程安全风险
    startTimeStamp = Stopwatch.GetTimestamp();
    // 计数包含线程池线程 + 主线程自身的处理任务
    pendingJobs.Reset(chosenNumberOfThreads + 1);

    for (int i = 0; i < chosenNumberOfThreads; i++)
    {
        ThreadPool.QueueUserWorkItem(_ => ProcessQueue());
    }

    // 主线程也参与队列处理
    ProcessQueue();

    // 等待所有处理线程执行完毕
    pendingJobs.Wait();
}

// 工作线程执行逻辑
void ProcessQueue() 
{
    // 线程安全出队,队列空自动退出循环
    while (workQueue.TryDequeue(out var job)) 
    {
        // 实时计算流逝时间,不需要加锁
        long elapsedMs = (long)((Stopwatch.GetTimestamp() - startTimeStamp) * 1000.0 / Stopwatch.Frequency);
        if (elapsedMs >= TimeoutMilliseconds) 
        {
            break;
        }
        job.DoWork();
    }
    pendingJobs.Signal();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 20:15:03