Why does a condition variable need a lock (and therefore also a mutex) [duplicate]
Asked Answered
B

2

20

Condition variables are one of the aspects of c++11 I'm still struggling with a bit. From what I have gathered a condition variable is very similar to a semaphore.

But then again, a semaphore wouldn't need a lock to function. A condition variable does. And a lock in turn needs a mutex. So in order to use the fairly simple functionality of a semaphore we now need to not only manage a condition variable. But also a mutex and a lock.

So why does a condition variable need this? And what added functionality is provided by adding this requirement?

Blueberry answered 12/5, 2015 at 14:52 Comment(4)
The mutex is the lock. The thread class lock is just a RAII wrapper around the mutex, so it is not managed but used locally.Biel
Related Why do pthreads' condition variable functions require a mutex?Flatboat
Tangentially related: You can implement semaphores in terms of mutexes and condition variables pretty easily. Condition variables are just a different waiting primitive; they make it much easier to wait on general, user-code expressed, conditions.Philip
This NOT a duplicate. This question is explicitly about C++11 and in that case the answers given for pthreads condition variable DO NOT APPLY. See also the bottom of Mike Vine's answer.Whereto
R
8

Condition variables are generally used to signal a change of state. A mutex is usually needed to make that change, and the following signal, atomic.

A semaphore encapsulates some state (a flag or counter) along with the signalling mechanism. A condition variable is more primitive, only providing the signal.

Rondarondeau answered 12/5, 2015 at 14:54 Comment(7)
At least in C++, there is no need to protect the signalling with a lock. In fact, depending on how the implementation handles the scheduling of the waiting thread, it might be harmful for performance to perform the signalling inside the lock. While there are certain conditions under which the program logic might rely on the fact that the state change and the signalling happen atomically, that is not in general true when using a condition variable.Tremaine
Isn't it the other way around? The a condition variable is a semaphore, on a mutex. Indicating whether the mutex is available again? Making the semaphore more primitive.Blueberry
@laurisvr: No, a semaphore combines an atomic flag/counter with a signal, so it's not primitive. A condition variable is just a signal, which you generally use in conjunction with some other thing whose state changes you want to signal.Rondarondeau
Okay let me rephrase:). The condition variable, requires some additional elements(the lock and it's respective mutex). The condition variable itself therefore is more basic, since it depends on the mutex for a flag. But the condition variable including the mutex and lock. Are larger, and generate more overhead than a traditional mutex. Is that about right?Blueberry
@laurisvr: I've no idea what you mean; of course a mutex plus other stuff is larger than a mutex. Or did you mean larger than a semaphore? That depends on how the semaphore, mutex and condition variable are implemented.Rondarondeau
@Mike Yeah maybe I'm splitting hairs here. But for example here #4792949. Semaphores are implemented using a condition variable. Is this the most optimal way to do this, because to me it feels like a semaphore doesn't really need all that stuff to function.Blueberry
@laurisvr: There's already a long discussion in the comments to that answer. There's no need for another one here.Rondarondeau
T
7

In general once you've signaled something has changed (via a condition variable) you need some code to run to handle that change and that code has to safely read the changed data. If you didn't have a lock associated with the cv then your thread waiting on the cv might wake up then try to (and fail) to acquire the lock associated with the data and therefore have to yield again. With a CV/Lock combo the underlying system can wake your thread up only if the thread can acquire the relevant lock as a unit and so be more efficient.

Its unlikely a CV on its own is useful as it gives no data above the fact it was signaled. If you imagine uses of cv - such as a thread-safe linked list with producers and consumers, you have variables representing {list, cv, lock} . In this case you take the lock, mutate the list, release the lock then signal the cv. On you consumer thread you'll very likely need to take the lock once signaled to act on the list, so having the lock acquired once you wake up from the CV being signaled is a good thing.

Look at something like events on windows (::CreateEvent) which are cv's without the implicit lock, a lot of the time they'll have a lock associated with them, but just not built into the actual usage.

Although this isn't the original reason condition variable in pthreads was created (they used the lock to protect the cv itself which is no longer needed in c++) the reason and usefulness of locks with cv's has migrated to whats in this answer.

Topheavy answered 12/5, 2015 at 15:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.