4

我有一个几年前用 C++(MFC、Visual Studio 6.0)构建的程序,并且已经在某台 Windows 机器上运行了相当长的一段时间(超过 5 年)。PC 在一个月前被更换(旧的已经死了),从那时起程序的计时行为发生了变化。我需要帮助理解为什么。

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

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

不同的击键有不同的等待时间,在 20 毫秒到 150 毫秒之间(取决于特定的击键,这是一个非常确定的时间)。时机非常重要。使用 simple 执行等待Sleep()。老电脑上休眠的准确度有1-2ms的偏差。我可以在计算机外部(在外部卡上)测量时间,所以我对睡眠时间的测量非常准确。请考虑到这台机器多年来每天执行数千次这样的 ON-sleep-OFF 循环,所以我拥有的准确度数据是合理的。

由于更换了PC,时间偏差超过10ms。

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

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

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

4

6 回答 6

3

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

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

可能是在您的旧机器上以某种方式更改了分辨率。

于 2009-09-12T19:51:46.770 回答
2

如果您主要关心的是精度,请考虑使用spinlock. Sleep()函数提示调度程序不要重新调度给定线程至少x毫秒,不能保证线程会在指定的时间内完全休眠。

于 2009-09-12T19:43:20.343 回答
1

通常 Sleep() 将导致延迟 ~15 毫秒或周期倍数 ~15 毫秒,具体取决于睡眠值。找出它如何工作的好方法之一是以下伪代码:

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

并且它还将表明此代码的行为对于例如 Windows XP 和 Vista/Win 7 是不同的

于 2009-09-12T21:54:18.333 回答
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 毫秒。非常准确的跨平台代码(尽管 Linux 上的准确度不同)并且非常易于阅读。

不过,我无法解释为什么您以前的 PC 如此准确;每当我使用它时,睡眠时间一直是 +/- 10 毫秒 - 如果 PC 很忙,情况会更糟。

于 2009-09-13T05:33:55.600 回答
0

睡眠取决于系统时钟。您的新机器可能与您以前的机器有不同的时间。从文档中:

此函数使线程放弃其时间片的剩余部分,并在基于 dwMilliseconds 值的时间间隔内变得不可运行。系统时钟以恒定速率“滴答”。如果 dwMilliseconds 小于系统时钟的分辨率,则线程可能休眠的时间少于指定的时间长度。如果 dwMilliseconds 大于 1 个滴答但小于 2 个滴答,则等待可能介于 1 到 2 个滴答之间,依此类推。要提高睡眠间隔的准确性,请调用 timeGetDevCaps 函数以确定支持的最小计时器分辨率,并调用 timeBeginPeriod 函数将计时器分辨率设置为最小值。调用 timeBeginPeriod 时要小心,因为频繁调用会显着影响系统时钟、系统电源使用和调度程序。

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

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

于 2009-09-12T19:33:56.633 回答
0

您的新 PC 是多核的,而旧的 PC 是单核的吗?计时精度的差异可能是使用了多线程和上下文切换。

于 2009-09-12T19:39:09.853 回答