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
相关产品推荐
相关产品推荐

