How std :: unique_lock and std :: condition_variable work

I need to clarify how lock and condition_variable work.

In -slyly modified-code here is cplusplusreference

std::mutex m; std::condition_variable cv; std::string data; bool ready = false; bool processed = false; void worker_thread() { // Wait until main() sends data std::unique_lock<std::mutex> lk(m); cv.wait(lk, []{return ready;}); // after the wait, we own the lock. std::cout << "Worker thread is processing data\n"; data += " after processing"; // Send data back to main() processed = true; std::cout << "Worker thread signals data processing completed\n"; // Manual unlocking is done before notifying, to avoid waking up // the waiting thread only to block again (see notify_one for details) lk.unlock(); cv.notify_one(); } int main() { std::thread worker(worker_thread); std::this_thread::sleep_for(std::chrono::seconds(1)); data = "Example data"; // send data to the worker thread { std::lock_guard<std::mutex> lk(m); ready = true; std::cout << "main() signals data ready for processing\n"; } cv.notify_one(); // wait for the worker { std::unique_lock<std::mutex> lk(m); cv.wait(lk, []{return processed;}); } std::cout << "Back in main(), data = " << data << '\n'; worker.join(); } 

What confused me was how the main thread could block the mutex if worker_thread already blocked it.

From this answer I saw that this is because cv.wait unlocks the mutex.

But now I'm confused by this: why do we even need to block it if cv.wait unlocks it?

For example, can I do this?

 std::unique_lock<std::mutex> lk(m, std::defer_lock); 

So, I create a lock object because it needs it, but I do not lock it when I create it.

Is there any difference now?

I did not understand why in this case I get a "runtime error" here .

+4
source share
3 answers

I will try to add a little more explanation regarding WHY variables that require locking.

You must have a lock because your code must verify that the condition predicate is true. A predicate is a value or combination of values ​​that must be true in order to continue. It can be a pointer that is NULL or points to a ready-made data structure, ready for use.

You must block it and CHECK the predicate before waiting, because by the time you start waiting for another thread, you may already have installed it.

A status notification and return of a wait DOES NOT mean that the condition is true. It only means that the condition WAS true at some time. Perhaps this was even true, then false, and then true. It could also mean that your thread was in an unrelated signal handler, which caused a wait state. Your code does not even know how many times a status notification has been called.

So, as soon as the wait condition returns it, the LOCKS mutex. Now your code can check the status, safely in the lock. If true, then the code can update what it needs to update and release the lock. If this is not true, it simply reverts to a wait state to retry. For example, it can take a pointer to a data structure and copy it into a vector, and then set the lock-protected pointer back to NULL.

Think about the condition to make the polling cycle more efficient. Your code still needs to do everything that it will do in anticipation of the loop, except that it can go into sleep mode instead of continuous rotation.

+2
source

Quote from std :: condition_variable :: wait () :

Calling this function if lock.mutex () is not blocked by the current thread is undefined behavior.

+4
source

I think your misunderstanding is due to a deeper misunderstanding of what locks are and how they interact with state variables.

The main reason blocking exists is to ensure mutual exclusion. Mutual exclusion ensures that certain parts of the code are executed by only one thread. That is why you cannot just wait with the lock until you need it to get your guarantees.

This causes problems when you want some other parts of the code to be executed, but still need to have a mutual exception while the current code snippet is running. The condition variables are convenient here: they provide a structured way to release the lock and ensure that when you wake up again, you will return it. This is why the lock is unlocked during the standby function.

+1
source

All Articles