更换电脑后睡眠()变得不太准确? (C ++)

我有几年前在C ++(MFC,Visual Studio 6.0)中构build的程序,并且已经在特定的Windows机器上运行了一段时间(超过5年)。 个人电脑在一个月前被更换(旧的一个人死亡),从那以后该程序的计时行为改变了。 我需要帮助理解为什么。

程序的主要function是通过向外部卡发送开启和closures信号来响应按键,并且在开启和closures之间具有非常准确的延迟。 一个示例程序stream程:

> wait for keystroke... > ! keystroke occurred > send ON message > wait 150ms > send OFF message 

不同的击键有不同的等待时间,在20ms到150ms之间(根据特定的击键时间确定的时间)。 时机非常重要。 等待使用简单的Sleep()来执行。 旧PC上的睡眠准确度是1-2ms的偏差。 我可以测量计算机外部的时间(在外部卡上),所以我对睡眠时间的测量非常准确。 请考虑这台机器执行这样的ON-sleep-OFF几十年,每天数千次,所以我的准确性数据是健全的。

由于PC被更换,时序偏差大于10ms。

我没有安装以前的电脑,所以可能会安装一些额外的软件包。 另外,我很惭愧地承认,我不记得以前的电脑是Windows 2000还是Windows XP。 我很确定这是XP,但不是100%(我现在不能检查…)。 新的是Windows XP。

我试图改变基于定时器的睡眠机制,但准确性没有改善。

什么都可以解释这个变化? 是否有可能已经安装在以前的PC上的软件包,可以解决这个问题? 有没有最好的做法来解决这个问题?

XP上的时间分辨率大约是10ms – 系统每10ms基本上“滴答”一次。 出于这个原因,睡眠不是一个很好的方法来做准确的时间。 我很确定win2000具有相同的分辨率,但如果我错了,可能是一个原因。

您可以更改该分辨率,至少为1毫秒 – 请参阅http://technet.microsoft.com/en-us/sysinternals/bb897569.aspx或使用此http://www.lucashale.com/timerresolution/ – 可能有一个注册表键(Windows媒体播放器也将更改该计时器,也许只有当它运行。

不知何故,你的旧机器上的分辨率可能会改变。

如果您主要关心的是精度,请考虑使用spinlockSleep()函数是调度程序不要将给定线程重新计划至少xms的提示,不能保证线程将在指定的时间内完全休眠。

通常睡眠()会导致延迟〜15毫秒或周期倍数约15毫秒,这取决于睡眠值。 关于找出haw的好方法,它的工作原理是下面的伪代码:

 while true do print(GetTickCount()); Sleep(1); end; 

而且还会显示这个代码的行为与Windows XP和Vista / Win 7不同

正如其他人所提到的,睡眠的准确性较差。

我通常使用Boost :: asio这种时机:

 // Set up the io_service and deadline_timer io_service io_ deadline_timer timer(io_service); // Configure the wait period timer.expires_from_now(posix_time::millisec(5)); timer.wait(); 

Asio为您的平台使用最有效的实施; 在Windows上,我相信它使用重叠的IO。

如果我将时间段设置为1ms并循环“计时器”。 通话10000次,通话时间一般在10005-10100毫秒左右。 非常准确,跨平台的代码(虽然在Linux上的准确性是不同的),非常容易阅读。

我不能解释为什么你以前的电脑是如此精确, 每当我使用它时,睡眠已经达到+/- 10毫秒 – 如果电脑很忙,就更糟。

你的新PC是多核还是旧的单核? 定时精度的差异可能是多线程和上下文切换的使用。

睡眠依赖于系统时钟。 你的新机器可能有一个不同于你以前的机器的时间。 从文档 :

该函数使线程放弃其时间片的剩余部分,并在基于dwMilliseconds值的时间间隔内变为不可运行。 系统时钟以固定速率“滴答”。 如果dwMilliseconds小于系统时钟的分辨率,线程可能会睡眠的时间少于指定的时间长度。 如果dwMilliseconds大于一个tick但小于两个,则等待可以是一个到两个tick之间的任何地方,依此类推。 要提高睡眠间隔的准确性,请调用timeGetDevCaps函数以确定支持的最小定时器分辨率和timeBeginPeriod函数,以将定时器分辨率设置为最小值。 调用timeBeginPeriod时要小心,因为频繁的调用会显着影响系统时钟,系统电源使用情况和调度程序。 如果您调用timeBeginPeriod,请在应用程序的早期一次调用它,并确保在应用程序的最后调用timeEndPeriod函数。

这些文件似乎意味着你可以试图使它更加准确,但是如果我是你,我不会去尝试。 只要使用一个计时器。

你用什么计时器取而代之? 如果你使用SetTimer(),那个定时器也很糟糕。
正确的解决方案是使用更高分辨率的TimerQueueTimer 。