【问题标题】:c++, usleep() is obsolete, workarounds for Windows/MingW?c++,usleep() 已过时,Windows/MingW 的解决方法?
【发布时间】:2011-08-13 16:40:54
【问题描述】:

我已经发现另一个问题,Windows/MingW 没有提供 nanosleep() 和 setitimer() 替代过时的 usleep()。 但我的目标是修复 cppcheck 给我的所有警告,包括 usleep() 样式的警告。

那么,是否有一种解决方法可以在不使用 cygwin 或安装大量新依赖项/库的情况下以某种方式避免 Windows 上的 usleep()?谢谢。

【问题讨论】:

  • 你用usleep做什么?
  • 相关问题:stackoverflow.com/questions/85122/…(我想建议使用 select 作为替代)
  • 使用 select 是个好主意。它不是非常有效,但是当一个人在等待时,谁在乎呢。另一方面,它尽可能便携。没有没有选择的严肃平台。
  • 只是盲目地尝试解决 cppcheck 发出的所有警告,而不考虑它们的基本原理以及这对您的项目意味着什么,这似乎不是最好的主意
  • usleep 没有什么过时的。

标签: c++ windows cppcheck usleep


【解决方案1】:

我使用的这段代码来自(最初来自here):

#include <windows.h>

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); 
}

请注意,SetWaitableTimer() 使用“100 纳秒间隔...正值表示绝对时间。...负值表示相对时间。”并且“实际计时器精度取决于关于你的硬件的能力。"

如果你有 C++11 编译器,那么你可以使用 this 便携版:

#include <chrono>
#include <thread>
...
std::this_thread::sleep_for(std::chrono::microseconds(usec));

向 Howard Hinnant 致敬,他设计了令人惊叹的 &lt;chrono&gt; 库(whose answer below 值得更多的爱。)

如果你没有 C++11,但你有 boost,那么你可以改为 this

#include <boost/thread/thread.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
...
boost::this_thread::sleep(boost::posix_time::microseconds(usec));

【讨论】:

  • 如果你有 C++14,你可以使用ms 用户定义的文字,如@9​​87654333@。这仅适用于常量值(显然),并且需要 using 指令。详情请见this answer
  • std::this_thread::sleep_for 的 GCC 实现将在支持时使用 ::nanosleep,但在 Windows 上以毫秒分辨率使用 ::Sleep。不知道 VS one。
【解决方案2】:

旧问题的新答案:

新答案的理由:工具/操作系统已经更新,现在有比最初提出问题时更好的选择。

C++11 &lt;chrono&gt;&lt;thread&gt; 标准头文件已经在 VS 工具集中存在好几年了。使用这些头文件,在 C++11 中最好将其编码为:

std::this_thread::sleep_for(std::chrono::microseconds(123));

我仅使用微秒作为示例持续时间。您可以使用任何方便的持续时间:

std::this_thread::sleep_for(std::chrono::minutes(2));

使用 C++14 和一些 using 指令,这可以写得更紧凑一点:

using namespace std::literals;
std::this_thread::sleep_for(2min);

或:

std::this_thread::sleep_for(123us);

这绝对适用于 VS-2013(以 chrono-literals 为模)。我不确定 VS 的早期版本。

【讨论】:

  • 就在前几天,我在一个使用 sleep_for 等待几微秒的简单测试程序中遇到了仅限 Windows 的死锁。 MS 的实现似乎可以归结为 Sleep(或等效的),这需要几毫秒。调用 sleep_for 不到一毫秒将转换为 Sleep(0)。 Sleep() 文档解释说,在某些情况下,从线程池线程调用 Sleep(0)可能会导致死锁,而这些情况在我们的测试套件中很常见。用毫秒调用 sleep_for 解决了问题。
  • 这听起来值得一个错误报告。实现应该将更精细的精度向上四舍五入到下一个可休眠的精度,而不是向下。
【解决方案3】:

Sleep() 函数的毫秒范围已得到很好的描述和很好的理解。它不会做任何不可预测的事情。有时该函数被归咎于执行不可预测的,即在延迟到期之前返回。我必须说这是错误的。仔细调查将确认它的行为是绝对可预测的。唯一的问题是 有很多关于它的读物,而且大部分都是幼稚的。也常说 windows不是实时操作系统。但是这样的 cmets 没有任何贡献,而且这样的 cmets 用于隐藏知识的缺乏。这让我有点生气,甚至 微软注意到了这一点并提供了更好的文档。

但是,不要夸大这个小答案:sleep() 函数是精确的,当以正确的方式使用并且知道它的特性时。必须特别注意 sleep(0)。这是一个非常强大的工具,尤其是与进程优先级类、线程优先级、多媒体计时器设置和处理器关联掩码一起使用时。

因此,在系统中断期间,通常可以轻松安全地执行真正的睡眠。当涉及比中断周期短的睡眠时,需要旋转。 为了在更短的时间内旋转,必须使用更高分辨率的时间源。 最常见的来源是性能计数器。 QueryPerformanceCounter(*arg) 提供递增的 *arg。 QueryPerformanceFrequency(*arg) 提供性能计数器递增的频率。这通常在 MHz 范围内,并且根据底层硬件而有所不同。 MHz 范围内的频率提供微秒级分辨率。这种方式可以使用高分辨率的东西来等待所需的时间跨度到期。但是,必须仔细检查其准确性:操作系统将性能计数器频率作为常数返回。这是错误的!由于频率是由物理设备生成的,因此始终存在偏移,也不是 持续的。它有热漂移。更现代的系统确实有更少的漂移。但如果热漂移仅为 1ppm,则误差为 1us/s。偏移量可以轻松达到几个 100。1MHz 中的 100 偏移量对应于 100us/s。

如果一个线程要等待高分辨率的任何时间,它应该建立一个服务线程。两个线程应共享一个命名事件。服务线程应在所需的休眠延迟之前休眠 1 个中断周期,然后在剩余的微秒内在性能计数器上旋转。当服务线程到达最终时间时,它设置命名事件并结束。调用线程将被唤醒,因为它正在通过等待函数等待指定事件。

总结:

  • 睡眠很好理解,但文档却很少。
  • 服务线程可以以高分辨率模拟睡眠。
  • 这样的服务线程可以建立为系统范围的服务。
  • 性能计数器的准确性需要仔细检查。需要校准。

更多详细信息,请访问Windows Timestamp Project

【讨论】:

  • 不完全!分辨率不一定是报告频率的倒数——在我的一个盒子上,QPF() 返回标称 CPU 时钟频率,但 QPC() 总是返回 10 的倍数。我猜这是由于实现了“不变 TSC”又名 constant_tsc.
  • @tc.:我是否建议不要相信 QPF 的结果?是的,我做到了,因为它的结果从不显示真相,它从不显示物理频率。它应被视为估计的constant。以 1Hz 为单位报告 CPU 时钟频率有点愚蠢。即使是 10 的单位也是荒谬的,因为实际频率会偏离几个 ppm。 1ppm 等于 2GHz 硬件上的 2000Hz。因此,QPF 输出的更现实的粒度应该/可能是频率的 1ppm。顺便说一句:这也会提示人们 QPF 的输出只是一个估计值
  • 我专门回应“MHz 范围内的频率提供微秒分辨率”,我认为您误解了我:我的结果表明 QPC() 在我的一个盒子上计数 10 步,给出分辨率约为 1/(240 MHz);不是 1/(2.4 GHz)。时钟漂移/歪斜完全是另一种蠕虫......
  • @tc.:嗯,1/240MHz 是 ~ 4ns。这也比微秒分辨率好得多。并且:当然分辨率也由量化/粒度决定(以上所有假设)。
  • @tc: ... 刚刚引起我的注意:constant TSC 将返回 QPF() 和 CPU 频率。/1024。 CPU 频率。硬件不存在,它是由倍频器(1024)产生的。
【解决方案4】:

这取决于您需要什么粒度。如果您说的是毫秒,那么 Win32 睡眠功能将完成这项工作 - 请参阅 http://msdn.microsoft.com/en-us/library/ms686298%28v=vs.85%29.aspx。如果您说的是微秒,那么没有简单的方法可以做到这一点,您很幸运能够在 Windows(不是 RTOS)或 Linux 上获得这种计时器分辨率。

【讨论】:

    【解决方案5】:

    我找到了this blog post about it。它使用QueryPerformanceCounter。发布的功能:

    #include <windows.h>
    
    void uSleep(int waitTime) {
        __int64 time1 = 0, time2 = 0, freq = 0;
    
        QueryPerformanceCounter((LARGE_INTEGER *) &time1);
        QueryPerformanceFrequency((LARGE_INTEGER *)&freq);
    
        do {
            QueryPerformanceCounter((LARGE_INTEGER *) &time2);
        } while((time2-time1) < waitTime);
    }
    

    我希望这会有所帮助。

    【讨论】:

    • 这是一个忙碌的等待。它会浪费 CPU 周期。
    • @tibor 是的。据我所知,Windows 进程调度程序不提供任何具有微秒精度的睡眠过程。不过,您可以创建一个混合睡眠功能,在毫秒内使用默认的睡眠功能,并在最后一个小等待时间使用忙等待。这可以在不牺牲精度的情况下将性能损失降低到不到 5 毫秒的忙等待时间。
    • 当前代码忽略了freq的值。相反,如果 waitTime 以微秒为单位,则最终测试应该是 time2-time1 &lt; waitTime*freq/1000000
    【解决方案6】:

    我参加聚会已经很晚了,但我只想为这个问题添加一些内容。如果您想使用微秒分辨率实现可移植性,而不是使用 select() 使用空文件描述符集的系统调用。它可以在 linux 和 windows 上运行,即可以使用统一的接口调用它(行为可能仍然不同,尤其是在 windows 上,你可以要求 1 微秒但你会得到 1ms 的睡眠)。如果您想使用第三方库,请使用 Boost,但要使用最新版本。与时间相关的 std api 只是一团糟,我在这里提供一个摘要:

    1. Windows:sleep_for、sleep_until、condition_variable、future 等 wait_for/wait_until 方法完全不可靠,因为它们基于 system_clock,因此如果时间发生变化,您的应用程序可能会挂起。他们在内部修复了这个问题,但由于它是 ABI 中断,所以他们还没有发布它,即使在最新的 VS 2019 上也不可用。
    2. Linux:sleep_for、sleep_until 都可以,但 condition_variable/future wait_for/wait_until 不可靠,因为它们也基于系统时钟。它已通过 Gcc 10 和 GlibC 2.6 bug link 修复。
    3. Boost:与上述相同,但修复了 1.67 版的时间问题。

    因此,如果您希望可移植代码制作自己的解决方案或仅使用 Boost 1.67+,请不要相信标准实现 c++11 chrono,因为执行的实现非常糟糕。

    【讨论】:

      【解决方案7】:

      usleep() 使用微秒。在获得微秒精度的窗口中,您应该使用QueryPerformanceCounter() winapi 函数。 Here 你可以找到如何使用它来获得这个精度。

      【讨论】:

      • 虽然 QueryPerformanceCounter 可以提供高分辨率的时钟值,但它不是一个实时睡眠 API -- 它只能让您忙着等待。
      • Windows 中没有什么是“实时的”。对于微秒或纳秒范围内的值,睡眠不是一个概念。 Windows 调度程序使用大约 10 毫秒的 quanta。除非您经常这样做,否则微秒级的繁忙等待并不可怕。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-05
      • 2020-10-19
      • 1970-01-01
      相关资源
      最近更新 更多