【问题标题】:Limits of Windows Queue TimersWindows 队列计时器的限制
【发布时间】:2022-03-19 04:01:23
【问题描述】:

我正在实现一个计时器,需要它每 50 毫秒左右运行一次,并且希望分辨率为 1 毫秒或更短。我首先阅读了这两篇文章:

http://www.codeproject.com/Articles/1236/Timers-Tutorial

http://www.virtualdub.org/blog/pivot/entry.php?id=272

奇怪的是,它们似乎相互矛盾。有人说队列计时器适用于高分辨率,其他帖子来自 Windows 7 系统,显示分辨率约为 15 毫秒(对于我的应用程序来说不够好)。

所以我在我的系统上进行了测试(Win7 64bit i7-4770 CPU @3.4 Ghz)。我从 50 毫秒开始,这就是我所看到的(从左侧开始的时间,右侧执行之间的间隔;全部以毫秒为单位):

150   50.00
200   50.01
250   50.00
...
450   49.93
500   50.00
550   50.03
...
2250  50.10
2300  50.01

我看到最大误差约为 100 us,平均误差可能约为 30 us。这让我相当高兴。

所以我开始放弃这个时期,看看它在什么时候变得不可靠。一旦我减少了时间

以 5 毫秒为周期,每隔几秒就会看到一些周期在 3 到 6 毫秒之间跳跃的情况并不少见。如果我将周期减少到 1 毫秒,则可以看到 5 到 10 到 40 毫秒的周期。我认为跳跃到 40 毫秒可能是由于我正在将内容打印到屏幕上,我不知道。

这是我的定时器回调代码:

VOID CALLBACK timer_execute(PVOID p_parameter, 
   BOOLEAN p_timer_or_wait_fired)
{ 
   LARGE_INTEGER l_now_tick;

   QueryPerformanceCounter(&l_now_tick);

   double now = ((l_now_tick.QuadPart - d_start.QuadPart) * 1000000) / d_frequency.QuadPart;
   double us = ((l_now_tick.QuadPart - d_last_tick.QuadPart) * 1000000) / d_frequency.QuadPart;

   //printf("\n%.0f\t%.2f", now / 1000.0f, ms / 1000.0f);

   if (us > 2000 ||
       us < 100)
   {
      printf("\n%.2f", us / 1000.0f);
   }

   d_last_tick = l_now_tick;
}

无论如何,只要您以 100hz 或更慢的速度执行,在我看来,队列计时器就是非常好的工具。我链接到的第二篇文章中发布的不良结果(精度为 15 毫秒左右)是否可能是由于 CPU 速度较慢或配置不同?

我想知道我是否可以在多台机器上获得这种性能(所有机器都与运行 64 位 Win7 的机器一样快或更快)?另外,我注意到如果你的回调在这段时间过去之前没有退出,操作系统将在那里放置另一个线程。这可能是显而易见的,但它在任何文档中对我来说并不突出,并且对客户端代码有重大影响。

【问题讨论】:

  • 我也在搜索这个,可以说 CreateWaitableTimer-SetWaitableTimer 可以提供更高的精度,它有一个 LARGE_INTEGER 的参数,其间隔为 100ns。你试过了吗? (好吧,您输入已经很久了,但您可能还记得。)

标签: visual-c++ timer windows-7 periodic-task


【解决方案1】:

Windows 默认计时器分辨率为 15.625 毫秒。这就是您观察到的粒度。 但是,可以按照 MSDN 中的说明修改系统计时器分辨率:Obtaining and Setting Timer Resolution。这允许在大多数情况下将粒度降低到大约 1 ms 平台。 This SO answer公开了如何获取当前系统定时器分辨率。

隐藏功能NtSetTimerResolution(...) 甚至允许在平台支持的情况下将定时器分辨率设置为 0.5 毫秒。请参阅this SO 回答问题“如何将计时器分辨率设置为 0.5 毫秒?

...不同的配置? 这取决于底层硬件和操作系统版本。使用上述工具检查计时器分辨率。

...都和我运行 64 位 Win7 的机器一样快或快)? 是的你可以。但是,也允许其他应用程序设置定时器分辨率。谷歌浏览器是一个已知的例子。这样的其他应用程序也可以仅临时改变定时器分辨率。因此,您永远不能依赖计时器分辨率 跨平台/时间是一个常数。 确保计时器分辨率的唯一方法是 由您的应用程序控制的是将计时器粒度设置为您自己的最小值为 1 毫秒(0.5 毫秒)。

注意:降低系统定时器粒度会导致系统中断频率增加。它减少了线程量子(时间片)并增加了功耗。

【讨论】:

  • 在我的具体情况下,我正在运行 Prepar3d 飞行模拟器应用程序和我的软件。我猜 P3d 会影响计时器分辨率。将计时器分辨率设置为 46.875 (3 * 15.625) 似乎给了我所需的执行周期,无论计时器分辨率发生了什么变化(除非应用程序已将分辨率降低到 20 毫秒)。
  • @Ian:任何应用程序请求的最高计时器分辨率都将处于活动状态。没有其他应用程序可以降低分辨率。因此无法保证您选择的 3 x 15.625 毫秒无论如何都会保持不变。另一种应用可能——也只是暂时的——寻求更高的分辨率。你检查过实际分辨率吗?我怀疑 Lockheed Martins Prepare3D 将分辨率设置为高于 46.875 毫秒
  • 在 Arno:糟糕,我的意思是将我的事件周期设置为 46.875 毫秒,而不是设置操作系统计时器分辨率。然后,唯一一次计时器周期太远的情况是,如果另一个应用程序将分辨率设置为 10 毫秒或其他什么,那就是计时器分辨率,但我会看看我们的生产 PC 上会发生什么。为了安全起见,我可能会强制分辨率为 5 毫秒。谢谢。顺便说一句:当我尝试为@Arno 使用@ 符号时,它会在我保存时从评论中消失?
【解决方案2】:

我相信差异是由于系统中使用的资源管理。我刚刚在我必须为我的操作系统课程做的一次演示中了解到这一点。由于有许多进程正在运行,当时间太短时,它可能无法足够快地对进程进行排队。另一方面,当它有更多时间时,进程会及时排队,而且它与优先级有关。我希望这有点帮助。

【讨论】:

    猜你喜欢
    • 2013-04-19
    • 1970-01-01
    • 2010-12-05
    • 1970-01-01
    • 2014-11-21
    • 2020-06-24
    • 1970-01-01
    • 2014-09-02
    • 2014-06-29
    相关资源
    最近更新 更多