Visual c++ Windows队列计时器的限制

Visual c++ Windows队列计时器的限制,visual-c++,windows-7,timer,high-resolution,periodic-task,Visual C++,Windows 7,Timer,High Resolution,Periodic Task,我正在实施一个计时器,需要它运行每50毫秒左右,并希望分辨率为1毫秒或更少。我从阅读以下两篇文章开始: 奇怪的是,他们似乎互相矛盾。一个说队列计时器有助于提高分辨率,另一个则是Windows7系统的分辨率在15毫秒左右(对于我的应用程序来说还不够好) 所以我在我的系统上运行了一个测试(Win7 64位i7-4770 CPU@3.4 Ghz)。我从50毫秒开始,这是我看到的(从左边开始的时间,右边执行之间的间隔,都以毫秒为单位): 我看到最大误差约为100 us,平均误差约为30 us左右。这

我正在实施一个计时器,需要它运行每50毫秒左右,并希望分辨率为1毫秒或更少。我从阅读以下两篇文章开始:

奇怪的是,他们似乎互相矛盾。一个说队列计时器有助于提高分辨率,另一个则是Windows7系统的分辨率在15毫秒左右(对于我的应用程序来说还不够好)

所以我在我的系统上运行了一个测试(Win7 64位i7-4770 CPU@3.4 Ghz)。我从50毫秒开始,这是我看到的(从左边开始的时间,右边执行之间的间隔,都以毫秒为单位):

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


所以我开始降低周期,看看它在什么时候变得不可靠。当我缩短周期后,我开始看到不好的结果我相信这是因为系统中使用了资源管理。我刚刚在操作系统课上做的一次演讲中了解到了这一点。由于有许多进程正在运行,因此当时间太短时,它可能无法足够快地对进程进行排队。另一方面,当进程有更多的时间时,它就会及时排队,这也与优先级有关。我希望这会有所帮助。

Windows默认计时器分辨率为15.625毫秒。这是您观察到的粒度。 但是,可以按照MSDN:所述修改系统计时器分辨率。这允许在大多数情况下将粒度降低到1 ms左右 平台。所以答案揭示了如何获得当前系统计时器分辨率

隐藏函数
NtSetTimerResolution(…)
甚至允许在平台支持时将计时器分辨率设置为0.5毫秒。请参见“如何将计时器分辨率设置为0.5 ms?”问题的答案

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

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


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

在我的特定情况下,我正在运行Prepar3d飞行模拟器应用程序,并附带我的软件。我猜P3d会干扰计时器的分辨率。将计时器分辨率设置为46.875(3*15.625)似乎可以为我提供所需的执行时间,无论计时器分辨率发生了什么变化(除非应用程序将分辨率降低到20ms)。@Ian:任何应用程序请求的最高计时器分辨率都将处于活动状态。没有其他应用程序可以降低分辨率。因此,无法保证您选择的3 x 15.625毫秒在任何情况下都能保持不变。另一个应用程序可能(也只是暂时)需要更高的分辨率。你检查过实际的分辨率了吗?我怀疑洛克希德·马丁斯公司(Lockheed Martins Prepare3DAt Arno)将分辨率设置为高于46.875毫秒。阿诺:哎呀,我的意思是将事件周期设置为46.875毫秒,而不是将操作系统计时器分辨率设置为46.875毫秒。然后,唯一的时间计时器周期会太远,如果另一个应用程序设置的分辨率为10毫秒或什么,这是计时器的分辨率,但我会看看我们的生产PC上会发生什么。为了安全起见,我可以强制将分辨率设置为5毫秒。谢谢顺便说一句:当我尝试使用@Arno的@symbol时,它在我保存时会从注释中消失?我也在搜索它,可以说CreateWaitableTimer SetWaitableTimer可能具有更高的精度,它有一个大的\u整数参数,间隔为100ns。你试过了吗?(好的,从你开始已经有很长一段时间了,但你可能还记得。)
150   50.00
200   50.01
250   50.00
...
450   49.93
500   50.00
550   50.03
...
2250  50.10
2300  50.01
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;
}