c++, ussleep()已经过时,Windows/MingW的变通方法

c++, usleep() is obsolete, workarounds for Windows/MingW?

本文关键字:MingW 方法 Windows 过时 ussleep c++      更新时间:2023-10-16

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

那么,有没有一种方法可以避免在Windows 上使用ussleep()而不使用cygwin或安装新的依赖项/库?谢谢。

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

#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纳秒间隔…正数表示绝对时间. ...负值表示相对时间。"answers"实际的计时器精度取决于硬件的能力。"

如果你有c++ 11编译器,那么你可以使用这个移植版本:

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

向Howard Hinnant致敬,他设计了令人惊叹的<chrono>库(他的答案值得更多的爱)

如果你没有c++ 11,但你有boost,那么你可以这样做:

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

老问题的新答案:

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

c++ 11 <chrono><thread> std头文件已经在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(对时间字量取模)。我不确定VS.的早期版本

Sleep()函数的毫秒范围被很好地描述和理解。它不会做任何不可预测的事情。有时,该函数被指责为执行不可预测的操作,即在延迟过期之前返回。我得说这是错的。仔细的调查将证实它的行为是完全可以预测的。唯一的问题是关于它有很多值得一读的地方,而且大部分都是孩子气的。人们也常说windows不是实时操作系统。但这样的评论没有任何贡献,而且是这样评论是用来掩盖知识的缺乏。这让我有点生气,甚至微软注意到了这一点,并提供了更好的文档。

然而,不夸张这个小答案:sleep()函数是精确的,如果使用得当,并且知道它的特点。必须特别注意睡眠(0)。这是一个非常强大的工具,特别是在与进程优先级类、线程优先级、多媒体计时器设置和处理器关联掩码一起使用时。

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

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

概要:

  • 睡眠被很好地理解,但缺乏记录。
  • 服务线程可以在高分辨率下模拟休眠。
  • 这样的服务线程可以建立为系统范围的服务。
  • 性能计数器的准确性需要仔细观察。需要校准。

更多详细信息可以在Windows时间戳项目

中找到。

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

我找到了这篇关于它的博客文章。它使用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);
}

我希望这对你有帮助。

我来晚了,但我想对这个问题补充一些东西。如果您希望使用微秒分辨率实现可移植性,请使用空文件描述符集使用select()系统调用。它可以在linux和windows上工作,也就是说,它可以使用统一的接口调用(行为仍然可以不同,特别是在windows上,你可以请求1微秒,但你得到1毫秒的睡眠)。如果您想使用第三方库,请使用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是不可靠的,因为它们也基于系统时钟。
  3. Boost:与上面相同,但他们修复了1.67版本的时间问题。

因此,如果您想要编写自己的可移植代码或仅使用Boost 1.67+,请不要相信标准的c++11 chrono实现,因为执行的实现非常糟糕。

usleep()使用微秒。在windows中,为了获得微秒精度,你应该使用QueryPerformanceCounter() winapi函数。在这里你可以找到如何使用它获得精度