Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
242 views
in Technique[技术] by (71.8m points)

c++ - why do I need std::condition_variable?

I found that std::condition_variable is very difficult to use due to spurious wakeups. So sometimes I need to set a flags such as:

atomic<bool> is_ready;

I set is_ready to true before I call notify (notify_one() or notify_all()), and then I wait:

some_condition_variable.wait(some_unique_lock, [&is_ready]{
    return bool(is_ready);
});

Is there any reason that I shouldn't just do this: (Edit: Ok, this is really a bad idea.)

while(!is_ready) {
    this_thread::wait_for(some_duration); //Edit: changed from this_thread::yield();
}

And if condition_variable had chosen a waiting duration (I don't know whether this is true or not), I prefer choose it myself.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You can code this either way:

  1. Using atomics and a polling loop.
  2. Using a condition_variable.

I've coded it both ways for you below. On my system I can monitor in real time how much cpu any given process is using.

First with the polling loop:

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

std::atomic<bool> is_ready(false);

void
test()
{
    std::this_thread::sleep_for(std::chrono::seconds(30));
    is_ready.store(true);
}

int
main()
{
    std::thread t(test);
    while (!is_ready.load())
        std::this_thread::yield();
    t.join();
}

For me this takes 30 seconds to execute, and while executing the process takes about 99.6% of a cpu.

Alternatively with a condition_variable:

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

bool is_ready(false);
std::mutex m;
std::condition_variable cv;

void
test()
{
    std::this_thread::sleep_for(std::chrono::seconds(30));
    std::unique_lock<std::mutex> lk(m);
    is_ready = true;
    cv.notify_one();
}

int
main()
{
    std::thread t(test);
    std::unique_lock<std::mutex> lk(m);
    while (!is_ready)
    {
        cv.wait(lk);
        if (!is_ready)
            std::cout << "Spurious wake up!
";
    }
    t.join();
}

This has the exact same behavior except that during the 30 second execution, the process is taking 0.0% cpu. If you're writing an app that might execute on a battery powered device, the latter is nearly infinitely easier on the battery.

Now admittedly, if you had a very poor implementation of std::condition_variable, it could have the same inefficiency as the polling loop. However in practice such a vendor ought to go out of business fairly quickly.

Update

For grins I augmented my condition_variable wait loop with a spurious wakeup detector. I ran it again, and it did not print out anything. Not one spurious wakeup. That is of course not guaranteed. But it does demonstrate what a quality implementation can achieve.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...