condition_variable :: wait_for和wait_until使用chrono :: steady_clock但在PC睡着时跳过持续时间?

时间:2015-12-22 00:06:35

标签: c++ multithreading c++11

我有一个与此类似的问题:

Are there any STL functions that wait that use wallclock time instead of "machine awake" time?

我在下面写了一个简单的测试程序。其输出作为注释附加在main()函数之后。

#include <iostream>
#include <atomic>
#include <condition_variable>
#include <thread>
#include <chrono>

namespace Test1 {
    std::condition_variable cv1;
    std::mutex cv_m1;
    std::atomic<int> i1{ 0 };

    void WaitForTest(int durationSeconds)
    {
        std::unique_lock<std::mutex> lk(cv_m1);
        if (cv1.wait_for(lk, std::chrono::seconds(durationSeconds), []() {return i1 > 0;}))
            std::cerr << "Thread WaitForTest finished waiting. i == " << i1 << '\n';
        else
            std::cerr << "Thread WaitForTest timed out. i == " << i1 << '\n';
    }
}

namespace Test2 {
    std::condition_variable cv1;
    std::mutex cv_m1;
    std::atomic<int> i1{ 0 };

    void WaitUntilTest(int durationSeconds)
    {
        std::unique_lock<std::mutex> lk(cv_m1);
        if (cv1.wait_until(lk, std::chrono::steady_clock::now() + std::chrono::seconds(durationSeconds), []() {return i1 > 0;}))
            std::cerr << "Thread WaitUntilTest finished waiting. i == " << i1 << '\n';
        else
            std::cerr << "Thread WaitUntilTest timed out. i == " << i1 << '\n';
    }
}

void DisplayCounter()
{
    for (;;)
    {
        std::cout << std::chrono::steady_clock::now().time_since_epoch().count() << '\t';
        std::cout << std::chrono::system_clock::now().time_since_epoch().count() << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
}

int main()
{
    std::thread t0(DisplayCounter);
    std::thread t1(Test1::WaitForTest, 20);
    std::thread t2(Test2::WaitUntilTest, 20);
    t1.join(); 
    t2.join();
    t0.join();
}

/*
4823241754127   14507388260401666
4824249305761   14507388270471228
4825256111840   14507388280536768
4826265075762   14507388290623794
4827272062892   14507388300690885
4828278976945   14507388310756954
4829285939442   14507388320824912
4830292731972   14507388330889902
4831301325579   14507388340950061
4832325019583   14507388351168172
4833328913228   14507388361252847
4834344668038   14507388371893418
4835526960329   14507388383183227  // Put PC to sleep for several minutes here.
5239334902724   14507392428113950
5241228987291   14507392440270719
5242391680803   14507392453061650
5243860130948   14507392467439058
5245178576003   14507392479499693
Thread WaitUntilTest timed out. i == 5246249606694Thread WaitForTest timed out. i ==    14507392493628356
0
0
5247878128997   14507392507329527
5249076101379   14507392518267779
5250080155548   14507392528308436
*/

/*
6901440131309   14507409048031904
6902447235446   14507409058098709
6903453988153   14507409068147687
6904456435024   14507409078169356
6905459314613   14507409088221283
6906465590262   14507409098284729
6907472642259   14507409108354858
6908479366227   14507409118421845
6909485420590   14507409128482293
6910492000867   14507409138524952
6911494460464   14507409148547279
6912496188054   14507409158571820
6913503297528   14507409168712603
6914518360974   14507409178777637
6915520958927   14507409188797529
6916522928740   14507409273645140 // Put PC to sleep for several seconds here.
6925151268349   14507409284698259
6926154791268   14507409294728122
6927189995828   14507409306269589Thread WaitForTest timed out. i ==
Thread WaitUntilTest timed out. i == 0
0
6928477053195   14507409318749386
6929719714225   14507409331272114
6930891244389   14507409342498796
*/

在网页http://www.cplusplus.com/reference/condition_variable/condition_variable/wait_for/

它说

  

它的行为就像实现为:   return wait_until(lck,chrono :: steady_clock :: now()+ rel_time,std :: move(pred));

输出显示即使在PC处于睡眠状态时,steady_clock也会前进 问题:为什么condition_variable :: wait_until只计算PC唤醒时的持续时间?

1 个答案:

答案 0 :(得分:2)

没有一个完美的解决方案适合所有情况,但这是你大多数时候都想要的,并且它需要最少的烦恼才能让它以不同的方式工作。如果它根据墙壁时间工作,那么如果机器处于睡眠状态,当它重新醒来时,一切都会立即唤醒,不仅弄乱了每次睡眠的绝对时间,还弄乱了它们之间的相对时间。

如果您关心,请使用操作系统的电源管理API来挂接其状态转换,并在状态发生变化时执行您需要的任何操作。一种可能性是使用电源管理器线程注册所有条件变量,该线程在机器从睡眠状态恢复时对每个已注册的条件变量进行唤醒。