【问题标题】:Timer takes 10 ms more than interval计时器比间隔多花 10 毫秒
【发布时间】:2010-06-07 12:18:15
【问题描述】:

我正在使用间隔为 1 秒的计时器。 但是在我打印时间的计时器的滴答事件中,它始终是 62 或 65 毫秒。 我不明白为什么要多花 10 毫秒。

请有人调查一下。

这是我正在使用的代码:

static int _counter;
var _timer = new System.Timers.Timer(1000);
public Form1()
{
    InitializeComponent();           
    _timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
    _timer.Start();            
}

private void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
    Console.WriteLine(DateTime.Now.ToString("{hh:mm:ss.fff}"));          
    _counter++;
    if (_counter == 20)
        _timer.Stop();
}

这是输出:

{01:59:08.381}
{01:59:09.393}
{01:59:10.407}
{01:59:11.421}
{01:59:12.435}
{01:59:13.449}
{01:59:14.463}
{01:59:15.477}
{01:59:16.491}
{01:59:17.505}
{01:59:18.519}
{01:59:19.533}
{01:59:20.547}
{01:59:21.561}
{01:59:22.575}
{01:59:23.589}
{01:59:24.603}
{01:59:25.615}
{01:59:26.629}
{01:59:27.643}

【问题讨论】:

  • 你的问题描述和例子不一致。该示例设置为 1 秒间隔,并且似乎显示 1 秒间隔,而不是 50 毫秒

标签: c# .net timer


【解决方案1】:

您需要了解 Windows 不是实时操作系统。实时操作系统具有定时器机制,允许系统对定时器启动的事件何时发生以及与之相关的开销做出硬性保证,并允许您指定在错过最后期限时应该发生的行为——例如,如果之前的执行时间比间隔时间长。

当涉及到较小的间隔时,我会将 Windows 计时器描述为“尽力而为”。当间隔足够长时,您不会注意到您没有得到您请求的确切间隔。随着您越来越接近计时器的分辨率(计时器运行的频率),您开始将开销视为间隔增加的百分比。实时系统依赖于更复杂和更快的硬件解决方案,特别注意最大限度地减少软件开销。 Windows 计时器的确切频率取决于底层硬件提供的计时服务,因此可能因系统而异。

如果您有实时需求 - 每 50 毫秒执行一次操作可能属于该类别 - 那么您可能需要查看专门的硬件和/或实时操作系统。

【讨论】:

  • 先生,您不知道用户要求的具体计时器。此 Timer 默认不精确,与操作系统无关。 Windows 中还有其他更精确的计时器(例如多媒体计时器)。
  • @PavlosFragkiadoulakis 大声笑 - 不要将计时器的粒度与它的可预测性混淆。如果您需要严格的实时保证,Windows 不是您应该选择的操作系统。虽然它可能足以播放音乐,但我严重怀疑你会在上面驾驶飞机。
【解决方案2】:

这是因为系统时钟的分辨率有限。该事件在指定时间之后的下一个系统滴答声中发生,因此您总是会获得额外的几毫秒。

【讨论】:

    【解决方案3】:

    如果你需要一个更精确的计时器,你可以连接到 Win32 Multimedia Timer,它是最精确的计时器(低至 1ms)。这是一篇关于 CodeProject 的文章,展示了如何从 C# 中挂钩。

    【讨论】:

      【解决方案4】:

      首先,正如其他人所指出的,您将其设置为 1 秒,而不是 50 毫秒。

      其次,windows 不是实时操作系统。没有一个计时器类是完全精确的。你所做的只是说你想至少等这么久。一切都需要一些时间才能触发,并且一旦窗口开始实际处理滴答消息,您最终会通知计时器已滴答。

      【讨论】:

      • 您可以通过在绑定到单个 CPU 内核的线程中使用性能计数器来校准高分辨率计时器,从而使 Windows 表现得更好。我想。我已经看到它完成了,但没有编写相关代码,需要一些非常低级的技术(即,可能是直接机器代码!)并且在这方面存在邪恶的固件错误。最好不要再担心 IMO 的时钟精度……
      • 在处理 A/D 外围设备、传感设备和医疗设备时,计时精度非常重要。这是非常非常重要的。但是,正如@Donnie 指出的那样,Windows 不是 实时操作系统。如果您必须使用 Windows 操作系统,请尝试 WinCE 或 Windows Embedded。
      【解决方案5】:

      请注意,在大多数语言中,睡眠调用通常会指定进程唤醒的最短时间。在指定的时间过去后,该进程被放入队列中,并希望调度程序激活它。但是这种激活有时可能会延迟。我不确定 Timer 类,但我怀疑它可能会遇到类似的问题。
      您也许可以尝试提高进程的优先级以减少增加的时间。

      【讨论】:

        【解决方案6】:

        System.Timers.Timer 不是一个精确的计时器。尤其是当系统处于负载状态时,它可能会有更大的延迟。

        为了在您的示例中获得更好的准确性,更改时间测量代码以使用 Stopwatch 类。

        static int _counter;
                System.Timers.Timer _timer = new System.Timers.Timer(1000);
                Stopwatch sw;
                public Form1()
                {
                    InitializeComponent();           
                    _timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
                    _timer.Start();            
                    sw = Stopwatch.StartNew();
                }
        
                void _timer_Elapsed(object sender, ElapsedEventArgs e)
                {
                    Console.WriteLine(sw.ElapsedMilliseconds);          
                    _counter++;
                    if (_counter == 20)
                        _timer.Stop();            
        
                    sw.Reset();
                    sw.Start();
                }
        

        【讨论】:

          【解决方案7】:

          使用系统计时器总是比请求的值长一点。这是由于系统中其他进程的开销造成的。

          【讨论】:

            【解决方案8】:

            在我的系统上是 14 毫秒。用谷歌搜索过;区别在于上下文线程切换延迟。有一篇关于高分辨率定时器的文章here

            【讨论】:

              【解决方案9】:

              正如其他响应者所提到的,Windows 不是实时操作系统。如果必须使用 windows,请尝试使用 Win CE 或 Windows Embedded。

              -S!

              【讨论】:

                【解决方案10】:

                时间的准确性可能取决于运行的进程数。如果你有这个选项,我会一一减少在你的计算机上运行的进程数量,我的意思是那些消耗大量 cpu 时间的进程,我会检查时间是否有所改善。尤其是浏览器、病毒​​扫描程序、后台运行的程序。

                【讨论】:

                  【解决方案11】:

                  这些偏差是正常的,因为它们不是 RTOS(实时操作系统)。这是我在这种情况下找到的最佳解决方案:Link

                  Program.MicroTimer microTimer = new Program.MicroTimer();
                  microTimer.MicroTimerElapsed += new Program.MicroTimer.MicroTimerElapsedEventHandler(OnTimedEvent);
                  microTimer.Interval = 1000; // Call micro timer every 1000µs (1ms)
                  
                  // Can choose to ignore event if late by Xµs (by default will try to catch up)
                  // microTimer.IgnoreEventIfLateBy = 500; // 500µs (0.5ms)
                  
                  microTimer.Enabled = true; // Start timer
                  System.Threading.Thread.Sleep(2000);
                  microTimer.Enabled = false;
                  

                  这些是代码 sn-ps。您可以尝试它们以查看控制台中的值。

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2019-01-19
                    • 2010-12-26
                    • 1970-01-01
                    • 2013-01-01
                    相关资源
                    最近更新 更多