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

时间:2019-02-02 12:24:08

标签: 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;
}

我使用Visual Studio命令行工具对此进行了编译,如下所示:

cl /EHsc /std:c++17 /O2 sleep_for.cpp

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

更新1。 看来这是一个操作系统问题。除了std::thread::sleep_forstd::condition_variable::wait_until,我还尝试了基于uvw library的计时器,结果相同。无论我做什么,我都不能使线程睡眠少于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/

2 个答案:

答案 0 :(得分:3)

documentation

  

至少在指定的sleep_duration内阻止当前线程的执行。   此功能可能会阻塞比由于调度或者资源争用延迟sleep_duration更长。

答案 1 :(得分:1)

此答案是根据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;
}

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

cl /EHsc /std:c++17 /O2 sleep_for.cpp winmm.lib

谢谢杰里米!

P.S。 增加计时器频率有其代价: https://randomascii.wordpress.com/2013/07/08/windows-timer-resolution-megawatts-wasted/