什么线程睡眠方法最精确:Monitor.Wait vs System.Timer vs DispatchTimer vs Threading.Timer

时间:2012-05-15 22:31:16

标签: c# .net multithreading timer threadpool

什么.NET对象(或技术)在每XXX毫秒启动一个线程时最精确?有什么权衡?

例如:

        int maxDurationMs = 1000;
        while (true)
        {
            DateTime dt = DateTime.UtcNow;
            DoQuickStuff()
            TimeSpan duration1 =  DateTime.UtcNow - dt;
            int sleepTime = maxDurationMs - duration1.Milliseconds;
            if (sleepTime > 0)
                System.Threading.Thread.Sleep(sleepTime);
        }

       // CPU Intensive, but fairly accurate
       int maxDurationMs = 1000;
        while (true)
        {
            DateTime dt = DateTime.UtcNow;
            DoQuickStuff()
            while (true)
            {
                if (dt.AddMilliseconds(maxDurationMs) >= DateTime.UtcNow)
                    break;
            }
        }

执行相同操作的替代方法,但具有不同程度的准确性和权衡(CPU等)

5 个答案:

答案 0 :(得分:4)

我自己从未实际使用它们,但据说Multimedia Timers具有Windows中任何计时器服务的最佳分辨率。 .NET BCL没有这个计时器服务的包装器,所以你必须自己进行P / Invoke调用。

另一种选择可能是在紧密循环中使用Stopwatch和一些标准Thread.Sleep调用。我不确定这种方法会带来多少运气,但它本身可能比普通的Thread.Sleep调用更准确。我从未尝试过,但我认为任何事情都值得一试。

我做了一些实验,我发现将线程优先级更改为ThreadPriority.Highest会产生很大的不同。它在我尝试的每种技术上都减少了间隔的标准偏差。

答案 1 :(得分:4)

不要使用DateTime:在大多数系统上,其精度限制在16ms左右。 (参见Eric Lippert's博客)

最准确的方法是让一个专用线程运行带有System.Diagnostics.Stopwatch对象的while循环来计算时间。

即使使用最精确和准确的计时器,考虑到CPU时间片的不可预测性,每隔x毫秒准确地提升一个事件并不是一项简单的任务:我建议调查游戏如何进行主循环(通过延迟实现稳定的30fps)例如,补偿)。一个很好的例子是OpenTK的GameWindow,特别是RaiseUpdateFrame方法。

答案 2 :(得分:2)

如果您希望precise intervals Windows Timers可能不是您需要使用的,可能某种RTOS更适合您。

从上面链接:

  

创建Timer API是为了解决当前可用计时器的问题......但是,Windows计时器并不像应用程序可能需要的那样准确。尽管Windows计时器消息可以以毫秒精度进行调度,但它们很少能够提供该结果,因为Windows计时器的准确性取决于系统时钟和当前活动。因为WM_TIMER消息是以低优先级处理的,有点像WM_PAINT消息,所以它们经常在处理其他消息时被延迟。

答案 3 :(得分:2)

在我的窗口服务定期操作我使用Monitor.Wait,因为它释放了一个线程并允许我在完成之前执行操作而不用担心下一个“计时器滴答”。 有了这个,我得到+/- 1毫秒的精度。如果一切顺利的话。

但是如果你需要完全精确,你可以指望你不应该使用.NET。实际上你不应该使用Windows。

可能会推迟您的流程(或线程)。

答案 4 :(得分:0)

System.Timers.Timer对象的基本实现偏差约为120 MS,导致我每分钟跳过至少一秒。

我可以使用以下技术在1分钟的间隔内在1毫秒内准确计时器。较短的间隔可能无法达到相同的准确性(加上DoWork()的开销在此效率中起作用)

 public class SystemTimerTest
   {

    readonly System.Timers.Timer timerRecalcStatistics;
    readonly System.Diagnostics.Stopwatch stopwatchForRecalcStatistics = new System.Diagnostics.Stopwatch();


    public SystemTimerTest(TimeSpan range, DataOverwriteAction action)
    {
        int recalculateStatisticsEveryXMillseconds = 1000;

        timerRecalcStatistics = new System.Timers.Timer(recalculateStatisticsEveryXMillseconds);
        timerRecalcStatistics.AutoReset = true;
        timerRecalcStatistics.Elapsed += new System.Timers.ElapsedEventHandler(TimerRecalcStatisticsElapsed);
        timerRecalcStatistics.Interval = recalculateStatisticsEveryXMillseconds;
        timerRecalcStatistics.Enabled = true;


        this.maxRange = range;
        this.hashRunningTotalDB = new HashRunningTotalDB(action);
        this.hashesByDate = new HashesByDate(action);
        this.dataOverwriteAction = action;
    }


    private void TimerRecalcStatisticsElapsed(object source, System.Timers.ElapsedEventArgs e)
    {
        stopwatchForRecalcStatistics.Start();
        Console.WriteLine("The TimerRecalcStatisticsElapsed event was raised at {0}", e.SignalTime.ToString("o"));

         // DO WORK HERE


        stopwatchForRecalcStatistics.Stop();
        double timeBuffer  = GetInterval(IntervalTypeEnum.NearestSecond, e.SignalTime) - stopwatchForRecalcStatistics.ElapsedMilliseconds;

        if (timeBuffer > 0)
            timerRecalcStatistics.Interval = timeBuffer;
        else
            timerRecalcStatistics.Interval = 1;

        stopwatchForRecalcStatistics.Reset();         
        timerRecalcStatistics.Enabled = true;
    }
 }

我想知道每1秒循环丢失1到120毫秒是否意味着CPU的效率不如此实现。