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

时间:2009-09-12 19:27:42

标签: c++ windows timer sleep

几年前我有一个用C ++(MFC,Visual Studio 6.0)构建的程序,并且已经在某台Windows机器上运行了很长时间(超过5年)。个人电脑在一个月前被取代了(旧电脑已经死了),从那以后,该计划的时间行为发生了变化。我需要帮助理解原因。

该程序的主要功能是通过向外部卡发送ON和OFF信号来响应击键,在ON和OFF之间具有非常准确的延迟。示例程序流程:

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

不同的击键有不同的等待时间,在20ms到150ms之间(非常确定的时间取决于特定的击键)。时机非常重要。使用简单的Sleep()执行等待。旧PC上睡眠的准确度为1-2ms偏差。我可以测量计算机外部的时间(在外部卡上),因此我对睡眠时间的测量非常准确。请注意这台机器多年来每天执行数千次ON-sleep-OFF循环,因此我的准确度数据是合理的。

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

我没有安装以前的PC,因此可能安装了一些其他软件包。另外,我很惭愧地承认我不记得以前的PC是Windows 2000还是Windows XP。我很确定它是XP,但不是100%(我现在无法检查......)。新的是Windows XP。

我尝试将睡眠机制更改为基于计时器,但准确性没有提高。

有什么可以解释这种变化吗?是否有可能已安装在以前的PC上的软件包可以解决问题?是否有最佳实践来解决问题?

6 个答案:

答案 0 :(得分:3)

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

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

可能是你的旧机器上某种方式改变了分辨率。

答案 1 :(得分:2)

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

答案 2 :(得分:1)

通常Sleep()会导致延迟〜15 ms或周期倍数〜15ms,具体取决于睡眠值。 关于找出它的好方法,它的工作原理如下:

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

此外,它还会显示此代码的行为与Windows XP和Vista / Win 7不同

答案 3 :(得分:1)

正如其他人所说,睡眠的准确性很高。

我通常使用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 ms。非常准确的跨平台代码(虽然在Linux上的准确性不同)并且非常容易阅读。

我无法解释为什么你以前的PC如此准确;每当我使用它时,睡眠时间为+/- 10ms - 如果PC正忙,则更糟。

答案 4 :(得分:0)

睡眠取决于系统时钟。您的新机器可能与您以前的机器有不同的时间。来自documentation

  

此函数会导致一个线程   放弃剩余的时间   切片,成为一个不可挽回的   基于值的区间   dwMilliseconds。系统时钟   以固定的速度“嘀嗒”。如果   dwMilliseconds小于   解析系统时钟,   线程可能睡不到了   指定的时间长度。如果   dwMilliseconds大于1   嘀嗒但不到两个,等待即可   在一到两个刻度之间,   等等。提高准确性   睡眠间隔,呼叫   timeGetDevCaps函数来确定   支持的最小计时器分辨率   和timeBeginPeriod函数   将计时器分辨率设置为   最小。打电话时要小心   timeBeginPeriod,因为频繁的调用可以   显着影响系统时钟,   系统电源使用情况和调度程序。   如果你调用timeBeginPeriod,请调用它   在应用程序的早期一次   一定要调用timeEndPeriod   功能在最后   应用

文档似乎暗示您可以尝试使其更准确,但如果我是你,我不会尝试。只需使用计时器。

你用什么计时器替换它?如果你使用了SetTimer(),那个计时器也很糟糕 正确的解决方案是使用更高分辨率的TimerQueueTimer

答案 5 :(得分:0)

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