Why std :: condition_variable notification and wait functions need a locked mutex

In my endless quest to understand std::contion_variable I came across the following. This page says the following:

 void print_id (int id) { std::unique_lock<std::mutex> lck(mtx); while (!ready) cv.wait(lck); // ... std::cout << "thread " << id << '\n'; } 

And after that he says this:

 void go() { std::unique_lock<std::mutex> lck(mtx); ready = true; cv.notify_all(); } 

Now, as I understand it, both of these functions will stop at the line std::unqique_lock . Until a unique castle is found. That is, no other thread has a lock.

So first we say that the print_id function print_id executed. The unique lock will be activated and the function will stop on the standby line.

If the go function is then executed (in a separate thread), the code will stop there on a unique lock line. Since the mutex is locked by the print_id function.

Obviously, this would not work if the code were like that. But I really don't understand what I will not get here. So please enlighten me.

+4
c ++ multithreading locking mutex c ++ 11
source share
3 answers

What you are missing is that wait unlocks the mutex and then waits for the signal to cv .

It again blocks the mutexes before returning.

You could find this by clicking wait on the page where you found an example:

When a thread is blocked, the function automatically calls lck.unlock (), allowing other blocked threads to continue working.

After notification (obviously, by some other thread), the function unlocks and calls lck.lock (), leaving lck in the same state as when the function was called.

+9
source share

There, one point that you missed is called wait() , which unlocks the mutex. The thread is atomic (releases the mutex + goes into sleep mode). Then, when it wakes up with a signal, it tries to intercept the mutex (possibly blocks); as soon as he acquires it, he can continue.

Note that there is no need to block the mutex for calling notify_* , only for wait*

+3
source share

In order to answer the question posed that is necessary regarding the claims that you should not receive notification blocking for performance reasons (is this more important than performance?): The need to block “wait” and recommendations to always block “notification” is to protect the user from himself and his program from data and logic races. Without blocking the “go”, the program you published will immediately have a “ready” data race. However, even if the finished ones themselves were synchronized (for example, atomic ones), you will have a logical race with a missed notification, because without blocking in "go" it is possible for the notification to appear immediately after the "ready" check and immediately before the actual wait, but the waiting one the flow may remain blocked indefinitely. Synchronizing the atomic variable itself is not enough to prevent this. This is why helgrind will warn when notification is made without committing. There are some cases where a mutex lock is not really required around a notification. In all these cases, bi-directional synchronization must be performed in advance so that the producing thread can know for sure that another thread is already waiting. IMO these cases are for experts only. In fact, I saw an expert talking about multithreading, I’m mistaken - he thought that there was enough atomic counter. However, locking around a wait is always necessary for correctness (or at least an operation that is atomic with a wait), and that is why the standard library provides it and atomically unlocks the mutex when it enters the wait.

POSIX state variables, unlike Windows events, are not "idiotic" because they have no state (except that they know about expectations). It is recommended that you use notification lock to protect you from the worst and most common disgusts. Of course, you can create an event with a Windows state similar to a state using the var + bool mutex + condition variable if you like.

0
source share

All Articles