3

什么 .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等)

4

5 回答 5

4

我自己从未真正使用过它们,但据说多媒体定时器在 Windows 中的任何定时器服务中具有最佳分辨率。.NET BCL 还没有此计时器服务的包装器,因此您必须自己执行 P/Invoke 调用。

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

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

于 2012-05-16T02:29:35.510 回答
4

不要使用 DateTime:在大多数系统上,它的精度限制在 16 毫秒左右。(参见Eric Lippert 的博客)

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

即使使用现有的最精确和最准确的计时器,鉴于 CPU 时间片的不可预测性,每 x 毫秒准确地引发一个事件也不是简单的任务:我建议研究游戏如何执行其主循环(通过延迟补偿实现稳定的 30fps,对于实例)。一个很好的例子是 OpenTK 的GameWindow,特别是 RaiseUpdateFrame 方法。

于 2012-05-16T15:59:41.587 回答
2

如果您想要精确的间隔 Windows 计时器可能不是您需要使用的,可能某种RTOS会更适合您。

从上面的链接:

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

于 2012-05-15T22:56:11.397 回答
2

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

但是,如果您需要可以信赖的完美精度,则不应使用 .NET。其实你不应该使用Windows。您的进程(或线程)总是有可能在执行中被推迟。

于 2012-05-15T23:12:47.997 回答
0

System.Timers.Timer 对象的基本实现倾斜了大约 120 毫秒,导致我每分钟至少跳过一秒。

我可以使用以下技术以 1 分钟的间隔在 1ms 内获得准确的计时器。更短的间隔可能无法达到相同的精度(加上 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 的效率不如此实现所能达到的效率。

于 2012-05-17T20:01:07.080 回答