与 i7 处理器相比,使用 Windows API CreateWaitableTimer 休眠在 Xeon 上的执行方式不同

Tot*_*son 4 c++ windows time c++builder

我正在开发一个需要持续约 1 毫秒睡眠的程序。睡眠用于生成长度约为 1 毫秒的硬件脉冲。

我正在使用以下代码进行睡眠

void usleep(__int64 usec)
{
  HANDLE timer;
  LARGE_INTEGER ft;
  ft.QuadPart = -(10*usec); // Convert to 100 nanosecond interval, negative value indicates relative time

  timer = CreateWaitableTimer(NULL, TRUE, NULL);
  SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
  WaitForSingleObject(timer, INFINITE);
  CloseHandle(timer);
}
Run Code Online (Sandbox Code Playgroud)

取自这里

当我在 Intel i7 上使用上面的代码(使用 Embarcaderos bcc32 编译器)时,通过 1000 (1ms) 我得到一个睡眠,我使用Poco 的时间戳函数测量到大约 1ms。代码本身在一个线程中执行。

代码如下所示:

    mDebugFile <<  std::setprecision (17) << mPulseEventTime.elapsed()/1000.0 << "\t" << 0 << "\n";
    setHigh(false);
    mDebugFile <<  std::setprecision (17) << mPulseEventTime.elapsed()/1000.0 << "\t" << 1 << "\n";       
    usleep(1000);
    mDebugFile <<  std::setprecision (17) << mPulseEventTime.elapsed()/1000.0 << "\t" << 1 << "\n";
    setLow(false);
    mDebugFile <<  std::setprecision (17) << mPulseEventTime.elapsed()/1000.0 << "\t" << 0 << "\n";
Run Code Online (Sandbox Code Playgroud)

其中 mDebugFile 是一个 fstream 对象,而 setLow/setHigh 是对硬件的调用。

但是,当在Xeon CPU上执行相同的代码时,睡眠时间测量为大约10 毫秒。假设 Poco 的计时函数给出了合适的时间,10 ms 是相当大的,当要求 1ms 时。

有没有其他方法可以让可靠的睡眠持续约 1 毫秒?能否修改 Windows 操作系统以提供更可靠的睡眠?

我无权使用 boost 或更高版本的 C++11 功能。

Adr*_*thy 5

Windows 不是实时操作系统,因此永远无法保证在用户代码中获得超精确的睡眠时间。有很多东西在玩。

  • 操作系统可能会改变到期时间以节省电量。这称为计时器合并。为处理一些事件而唤醒一次比在更高频率下更频繁地唤醒更节能。如果操作系统对不同类型的硬件使用不同的策略,那就不足为奇了。

  • WaitForSingleObject(和相关 API)仍然受调度程序的约束。当对象发出信号时,等待线程用于调度,但这并不意味着它会立即被调度。这取决于进程和线程优先级、可用内核、系统的量子间隔、月相等。

有两个 API 可以增加 Windows 上的默认计时器分辨率: NtSetTimerResolution (官方未记录)或timeBeginPeriod。如果机器没有过载,您可以使用基本方法(例如,睡眠)在用户代码中获得非常接近 1 毫秒的间隔。

但是很多程序在滥用这些 API 时会浪费能源。(如果您在机器上看到 1 毫秒的分辨率,很可能是因为某些程序已经提高了计时器分辨率。)长期以来,这一直被认为是不好的做法。计时器分辨率是系统范围的设置,因此它会影响机器上运行的所有内容。如果您必须提高分辨率,请仅在需要时才这样做,并确保在完成后立即恢复默认值。

如果您需要更高的精度,我相信您需要制作内核模式驱动程序,但我对此没有任何经验。在某些时候,Windows 添加了一个音频堆栈,以保证 20 us 或更少的延迟。如果我没记错的话,那需要内核模式的恶作剧。

您可能会考虑使用微控制器(例如,Arduino)来生成硬件信号,并让您的 Windows 程序通过串行接口向其发送命令。