windows WinXP下如何获得准确的1ms Timer Tick

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3361059/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-15 14:55:11  来源:igfitidea点击:

How to get an accurate 1ms Timer Tick under WinXP

c++windowswinapitimerreal-time

提问by schoetbi

I try to call a function every 1 ms. The problem is, I like to do this with windows. So I tried the multimediatimer API.

我尝试每 1 毫秒调用一次函数。问题是,我喜欢用 Windows 来做这个。所以我尝试了多媒体计时器 API。

Multimediatimer API

多媒体定时器API

Source

来源

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

My result was that most of the time the 1 ms was ok, but sometimes I get the double period. See the little bump at around 1.95ms multimediatimerHistogram http://www.freeimagehosting.net/uploads/8b78f2fa6d.png

我的结果是大部分时间 1 毫秒都可以,但有时我会得到双周期。查看大约 1.95 毫秒的小凸起多媒体计时器Histogram http://www.freeimagehosting.net/uploads/8b78f2fa6d.png

My first thought was that maybe my method was running too long. But I measured this already and this was not the case.

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

Queued Timers API

排队定时器 API

My next try was using the queud timers API with

我的下一次尝试是使用队列计时器 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);

But also the result was not as expected. Now I get most of the time 2 ms cycletime. queuedTimer http://www.freeimagehosting.net/uploads/2a46259a15.png

但结果也不尽如人意。现在我大部分时间得到 2 ms 的周期时间。 queuedTimer http://www.freeimagehosting.net/uploads/2a46259a15.png

Measurement

测量

For measuring the times I used the method QueryPerformanceCounter and QueryPerformanceFrequency.

为了测量时间,我使用了 QueryPerformanceCounter 和 QueryPerformanceFrequency 方法。

Question

So now my question is if somebody encountered similar problems under windows and maybe even found a solution?

所以现在我的问题是,是否有人在 windows 下遇到过类似的问题,甚至可能找到了解决方案?

Thanks.

谢谢。

采纳答案by Didier Trosset

Without going to a real-time OS, you cannotexpect to have your function called every1 ms.

如果不使用实时操作系统,您就不能期望1 毫秒调用一次您的函数。

On Windows that is NOT a real-time OS (for Linux it is similar), a program that repeatedly read a current time with microsecond precision, and store consecutive differences in an histogram have a non-empty bin for >10 ms! This means that sometimes you will have 2 ms, but you can also get more between your calls.

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

回答by Arno

A call to NtQueryTimerResolution()will return a value for ActualResolution. In your case the actual resolution is almost certainly 0.9765625 ms. This is exactly what you show in the first plot. The second occurace of about 1.95 ms is more precisely Sleep(1)= 1.9531 ms = 2 x 0.9765625 ms

调用NtQueryTimerResolution()将返回ActualResolution的值。在您的情况下,实际分辨率几乎可以肯定为 0.9765625 毫秒。这正是您在第一个图中显示的内容。大约 1.95 ms 的第二次出现更精确Sleep(1)= 1.9531 ms = 2 x 0.9765625 ms

I guess the interrupt period runs at someting close to 1ms (0.9765625).

我猜中断周期运行在接近 1 毫秒(0.9765625)。

And now the trouble begins: The timer signals when the desired delay expires.

现在麻烦开始了:计时器在所需的延迟到期时发出信号。

Say the ActualResolutionis set to 0.9765625, the interrupt heartbeat of the system will run at 0.9765625 ms periods or 1024 Hz and a call to Sleepis made with a desired delay of 1 ms. Two scenarios are to be looked at:

假设ActualResolution设置为 0.9765625,系统的中断心跳将以 0.9765625 ms 周期或 1024 Hz 运行,Sleep并以所需的 1 ms 延迟调用。需要考虑两种情况:

  1. The call was made < 1ms (ΔT) ahead of the next interrupt. The next interrupt will not confirm that the desired period of time has expired. Only the following interrupt will cause the call to return. The resulting sleep delay will be ΔT + 0.9765625 ms.
  2. The call was made >= 1ms (ΔT) ahead of the next interrupt. The next interrupt will force the call to return. The resulting sleep delay will be ΔT.
  1. 调用是在下一个中断前 < 1ms (ΔT) 进行的。下一次中断将不会确认所需的时间段已经到期。只有以下中断会导致调用返回。由此产生的睡眠延迟将为 ΔT + 0.9765625 毫秒。
  2. 调用是在下一个中断前 >= 1ms (ΔT) 进行的。下一个中断将强制调用返回。由此产生的睡眠延迟将为 ΔT。

So the result depends a lot on when the call was made and therefore you may observe 0.98ms events as well as 1.95ms events.

因此结果在很大程度上取决于调用的时间,因此您可能会观察到 0.98 毫秒事件和 1.95 毫秒事件。

Edit:Using the CreateTimerQueueTimerwill push the observed delay to 1.95 because the timer tick (interrupt period) is 0.9765625 ms. On the first occurence of the interrupt, the requested duration of 1 ms has not quite expired, thus the TimerProcwill only be triggered after the second interrupt (2 x 0.9765625 ms = 1.953125 ms > 1 ms). Consequently, the queueTimer plot shows the peak at 1.953125 ms.

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

Note: This behavior strongly depends on the underlying hardware.

注意:此行为在很大程度上取决于底层硬件。

More details can be found at the Windows Timestamp Project

更多细节可以在Windows Timestamp Project 中找到

回答by n0rd

You can try to run timeBeginPeriod(1)at the program start and timeEndPeriod(1)before quitting. This probablycan enhance timer precision.

您可以尝试在程序启动和退出之前运行timeBeginPeriod(1)timeEndPeriod(1)。这可能可以提高计时器精度。