【问题标题】:Unpredictable behaviour of std::sleep_for on Windows 10Windows 10 上 std::sleep_for 的不可预测行为
【发布时间】:2019-02-02 12:24:08
【问题描述】:

最近我发现由于未知原因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/

【问题讨论】:

  • 当你放弃你的 CPU 量时,你不确定多久能取回它;您要求的时间只是一个提示。如果系统繁忙,你可能要等待任意长的时间。
  • 休眠线程在某种程度上是一种反模式。你到底想在这里做什么?你想解决什么问题?另见:Windows 7: overshoot C++ std::this_thread::sleep_forsleep_for 在 Microsoft 的 CRT 中是基于系统时钟的,因此如果时钟发生变化,您可能会看到更长的睡眠时间。您确定“环境没有任何变化”吗?
  • @Cody Gray♦ 这是一个非常有趣的链接。至少我知道其他人也遇到过同样的问题。
  • 你得到的唯一保证是,只要你要求,线程就会休眠至少。它可能会睡得更久,而您没有保证“更长”的上限。
  • 可能值得尝试在您的main() 顶部调用timeBeginPeriod(1),看看是否有任何改善。

标签: c++


【解决方案1】:

此答案是根据 Jeremy Friesner 的评论创建的。他建议在main() 的开头尝试timeBeginPeriod(1)。那成功了!我对程序进行了修改,它的运行速度提高了 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

谢谢,杰里米!

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

【讨论】:

    【解决方案2】:

    看着documentation

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

    【讨论】:

    • 形式上它是正确的,但它没有解释原因。它也没有多大帮助,除了让我重新思考设计并寻找 std::sleep_for 的替代品
    • 您可以认为sleep_for 更接近于yield_for_at_least、@Fedorov7890。但是,是的,您绝对应该重新考虑任何依赖于线程睡眠时间确定的设计。
    • 我想说这回答了你的问题。它没有给你你想知道的东西,但这是没有提出精确问题的错......
    • @Ulrich Eckhardt 是的,确实如此。但正如 ob1 所说,“这不是你要找的答案”
    猜你喜欢
    • 2021-12-25
    • 1970-01-01
    • 2017-12-15
    • 1970-01-01
    • 2011-05-25
    • 1970-01-01
    • 2020-09-18
    • 1970-01-01
    • 2017-09-09
    相关资源
    最近更新 更多