4

I need an example of using notify_all() method. Because I cannot understand how should it work.

Every waiting thread begins with code like that:

std::unique_lock<std::mutex> lock(mutex); condition_variable.wait(lock, [](){return SOMETHING;}); 

At the very beginning, waiting thread needs to acquire a mutex. So if there are more than one waiting thread, rest of them will wait to lock a mutex. So what is the purpose of using notify_all() if waiting threads stuck at locking mutex and do not execute a method wait() at all? These threads will wake up one by one instead of simultaneously.

2

1 Answer 1

11

The mutex guards the internal state of the condition_variable. Calling wait on the condition_variable causes the mutex to be unlocked. So while waiting, threads do not own the mutex.

When the wait completes, the mutex is again (atomically) acquired before the call to wait returns.

The threads are not contending on the mutex, they are contending on the condition itself.

You are free to unlock the lock as soon as you return from wait if you wish. If you want to allow multiple threads to synchronise on a condition, for example, this is how you would do it. You can also use this feature to implement a semaphore.

example:

This code processes things in batches of 10. Note that notify_all() goes after the unlock():

#include <condition_variable> #include <mutex> #include <iostream> #include <string> #include <thread> #include <chrono> #include <vector> void emit(std::string const& s) { static std::mutex m; auto lock = std::unique_lock<std::mutex>(m); std::cout << s << std::endl; } std::mutex m; std::condition_variable cv; int running_count = 0; void do_something(int i) { using namespace std::literals; auto lock = std::unique_lock<std::mutex>(m); // mutex is now locked cv.wait(lock, // until the cv is notified, the mutex is unlocked [] { // mutex has been locked here return running_count < 10; // if this returns false, mutex will be unlocked again, but code waits inside wait() for a notify() }); // mutex is locked here ++running_count; lock.unlock(); // we are doing work after unlocking the mutex so others can also // work when notified emit("running " + std::to_string(i)); std::this_thread::sleep_for(500ms); // manipulating the condition, we must lock lock.lock(); --running_count; lock.unlock(); // notify once we have unlocked - this is important to avoid a pessimisation. cv.notify_all(); } int main() { std::vector<std::thread> ts; for (int i = 0 ; i < 200 ; ++i) { ts.emplace_back([i] { do_something(i); }); } for (auto& t : ts) { if (t.joinable()) t.join(); } } 
Sign up to request clarification or add additional context in comments.

12 Comments

Do you mean that wait() method unlocks mutex?
@AkasataAkasata wait simultaneously unlocks the mutex and enters a wait state on the condition variable. when the condition variable is notified, all waiters re-acquire the lock in order to test the condition. I'll annotate the code.
So if they are re-acquiring the lock in order, notify_all() wakes them in order too, right?
@AkasataAkasata I don't think the order of unlocking is guaranteed by the standard. Some docs here: en.cppreference.com/w/cpp/thread/condition_variable/wait
@RichardHodges, Only one thread can hold the lock at any given time, and it is not possible for cv.wait() to return until the thread holds the lock. So, yeah, it wakes them all simultaneously, but "wake" only means that the threads are moved from the condition variable's queue to the mutex queue. They won't all get the lock and return from wait() at the same time.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.