Windows 10 上 std::sleep_for 的不可预测行为

Fed*_*890 5 c++

最近我发现不知什么原因std::this_thread::sleep_for可以比预期的睡眠时间长 10 倍。这不仅仅是一个或几个偶然的延迟,而是非常显着的效果,可以持续几分钟,并且还会影响std::condition_variable::wait_for

这是我的代码:

#include <thread>
#include <iostream>
#include <chrono>

using namespace std;

int main(int argc, char const *argv[])
{
    unsigned t = 0;
    auto start = ::std::chrono::steady_clock::now();
    for(unsigned i = 0; i < 100; ++i) {
        ::std::this_thread::sleep_for(chrono::milliseconds(1));
        t ^= i; // prevent overeager optimization
    }
    auto stop = ::std::chrono::steady_clock::now();
    auto elapsed = ::std::chrono::duration_cast<::std::chrono::milliseconds>(stop - start);
    ::std::cout << elapsed.count() << "\n";
    ::std::cerr << t << "\n";

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

我用 Visual Studio 命令行工具编译了这个,如下所示:

cl /EHsc /std:c++17 /O2 sleep_for.cpp
Run Code Online (Sandbox Code Playgroud)

当我执行它时,结果有时接近 150,有时接近 1500。在执行之间,环境没有任何变化。造成这种影响的原因可能是什么?

更新 1. 看起来这是一个操作系统问题。除了std::thread::sleep_forstd::condition_variable::wait_until我基于也试过定时器UVW库,具有相同的结果。无论我做什么,我都不能让线程休眠少于 15 毫秒。这种效果在系统启动后非常明显,而且很少出现。

概括。有些人建议:阅读文档!他们警告过你!std::this_thread::sleep_for可能不稳定!但另一方面,根据文档,如果它工作得更稳定,那就没有错,只需要知道如何实现这一点。Jeremy Friesner 提出了一个解决方案,我已经对其进行了测试,结果证明了 10 倍的性能和良好的平均稳定性。是否使用std::this_thread::sleep_for以及如何,由您决定。如果您正在开发救生设备等,我绝对不建议使用它。但在许多情况下,它可能非常有用。

一篇讨论提高稳定性和分辨率的代价的好文章:https : //randomascii.wordpress.com/2013/07/08/windows-timer-resolution-megawatts-wasted/

Fed*_*890 5

这个答案是根据 Jeremy Friesner 的评论创建的。timeBeginPeriod(1)他建议在年初尝试一下main()。成功了!我对程序进行了修改,它的运行速度提高了 10 倍。这是代码:

#include <thread>
#include <iostream>
#include <chrono>
#include <windows.h>

using namespace std;

int main(int argc, char const *argv[])
{
    timeBeginPeriod(1);
    unsigned t = 0;
    auto start = ::std::chrono::steady_clock::now();
    for(unsigned i = 0; i < 100; ++i) {
        ::std::this_thread::sleep_for(chrono::milliseconds(1));
        t ^= i; // prevent overeager optimization
    }
    auto stop = ::std::chrono::steady_clock::now();
    auto elapsed = ::std::chrono::duration_cast<::std::chrono::milliseconds>(stop - start);
    ::std::cout << elapsed.count() << "\n";
    ::std::cerr << t << "\n";

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

可以使用以下命令对其进行编译:

cl /EHsc /std:c++17 /O2 sleep_for.cpp winmm.lib
Run Code Online (Sandbox Code Playgroud)

谢谢,杰里米!

PS 增加计时器频率是有代价的: https://randomascii.wordpress.com/2013/07/08/windows-timer-resolution-megawatts-wasted/