如何在WinXP下获得精确的1ms定时器刻度

我试图每1毫秒调用一个函数。 问题是,我喜欢用windows做这个。 所以我尝试了multimediatimer API。

Multimediatimer API

资源

idTimer = timeSetEvent( 1, 0, TimerProc, 0, TIME_PERIODIC|TIME_CALLBACK_FUNCTION ); 

我的结果是,大部分时间1毫秒是好的,但有时我得到了双倍的时间。 看到1.95ms左右的小颠簸multimediatimerHistogram http://img.zgserver.com/c%2B%2B/www.freeimagehosting.net

我的第一个想法是,也许我的方法运行时间太长。 但是我已经测量过了,事实并非如此。

排队计时器API

我的下一个尝试是使用queud定时器API

 hTimerQueue = CreateTimerQueue(); if(hTimerQueue == NULL) { printf("Error creating queue: 0x%x\n", GetLastError()); } BOOL res = CreateTimerQueueTimer( &hTimer, hTimerQueue, TimerProc, NULL, 0, 1, // 1ms WT_EXECUTEDEFAULT); 

但结果并不如预期。 现在我大部分时间都是2 ms周期。 queuedTimer http://img.zgserver.com/c%2B%2B/www.freeimagehosting.net

测量

为了衡量我使用QueryPerformanceCounter和QueryPerformanceFrequency方法的时间。

所以现在我的问题是,如果有人在Windows下遇到类似的问题,甚至可能find一个解决scheme?

谢谢。

不用去实时的操作系统,你不能期望 1毫秒调用一次你的函数。

在不是实时操作系统的Windows上(对于Linux来说它是相似的),以微秒精度重复读取当前时间的程序,并且在直方图中存储连续差异的程序具有> 10ms的非空的bin! 这意味着有时你会有2毫秒,但你也可以获得更多的电话。

NtQueryTimerResolution()调用NtQueryTimerResolution()返回一个值。 在你的情况下,实际的分辨率几乎肯定是0.9765625毫秒。 这正是你在第一个情节展示的。 大约1.95ms的第二次事件更准确地说是Sleep(1) = 1.9531ms = 2×0.9765625ms

我猜中断周期接近1ms(0.9765625)。

现在麻烦就开始了:计时器在所需的延时结束时发出信号。

假设实际分辨率设置为0.9765625,系统的中断心跳将运行在0.9765625 ms周期或1024 Hz,并且以1 ms的期望延迟进行Sleep呼叫。 有两种情况需要考虑:

  1. 在下一个中断之前调用<1ms(ΔT)。 下一个中断不会确认所需的时间已过。 只有下面的中断将导致该呼叫返回。 由此产生的睡眠延迟将是ΔT+ 0.9765625毫秒。
  2. 在下一个中断之前调用> = 1ms(ΔT)。 下一个中断将强制调用返回。 由此产生的睡眠延迟将是ΔT。

所以结果很大程度上取决于通话的时间,因此您可以观察到0.98ms的事件以及1.95ms的事件。

编辑:使用CreateTimerQueueTimer会将观察到的延迟推到1.95,因为计时器滴答(中断周期)是0.9765625毫秒。 在第一次发生中断时,所需的持续时间1 ms没有完全超时,因此TimerProc只会在第二次中断后触发(2 x 0.9765625 ms = 1.953125 ms> 1 ms)。 因此,queueTimer图显示在1.953125 ms的峰值。

注意:此行为强烈依赖于底层硬件。

更多细节可以在Windows时间戳项目中找到

您可以尝试在退出前在程序开始和timeEndPeriod(1)处运行timeBeginPeriod(1) 。 这可能会提高定时器的精度。