简体   繁体   中英

condition_variable::wait_for and wait_until using chrono::steady_clock but skipping duration while PC is asleep?

I have a question similar to this one:

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

I wrote a simple test program below. Its outputs are attached as comments after the main() function.

#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
*/

On web page http://www.cplusplus.com/reference/condition_variable/condition_variable/wait_for/

it says

It behaves as if implemented as: return wait_until (lck, chrono::steady_clock::now() + rel_time, std::move(pred));

The output shows that the steady_clock advances even when the PC is asleep.
Question: Why condition_variable::wait_until only count the duration when the PC is awake?

There's no one perfect solution that's right for all cases, but this is what you want most of the time and it requires the least fussing to get it to work a different way. If it worked based on wall time, then if the machine were asleep, when it woke back up, everything would wake all at once, messing up not only the absolute time of each sleep but the relative times between them as well.

If you care, use your operating system's power management API to hook its state transitions and do whatever you need when states change. One possibility is to register all condition variables with a power manager thread that does a wake on every registered condition variable when the machine resumes from sleep.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM