0

I'm trying to make threads where one triggers the other. However, I don't know how to use mutexes. I don't understand why a mutex is needed since I don't use shared variables. Therefore I don't understand which mutexes go to which thread.

void readLoop()
    {
        while (true)
        {
            std::unique_lock<std::mutex> lock(readThreadMutex);
            writeConditionVariable.wait(lock);
        }
    }

void writeLoop()
    {
        while (true)
        {
            std::unique_lock<std::mutex> lock(readThreadMutex);
            readConditionVariable.wait(lock);
        }
    }
Guerlando OCs
  • 1,886
  • 9
  • 61
  • 150
  • The only way to make a thread wait for a condition variable is to use a mutex. That's why you need to use mutexes. That's how C++ works, and it doesn't work any other way. – Sam Varshavchik Dec 08 '19 at 23:16
  • 1
    What exactly is unclear? For a generic example see the answer to [this question](https://stackoverflow.com/questions/16350473/why-do-i-need-stdcondition-variable) or the example at [cppreference](https://en.cppreference.com/w/cpp/thread/condition_variable). The mutex is required because `std::condition_variable` requires it, see the linked documentation on cppreference for explanation. "*which mutexes go to which thread*": The whole point is that there is only *one* mutex which the threads acquire using locks. – walnut Dec 08 '19 at 23:17
  • @walnut I can see condition_variable working without a mutex. It isn't clear why I need one in the case where `wait()` checks for no condition. The mutex only makes sense when a condition is passed on `wait` which means the wait function acesses data that can also be acessed outside the thread. Am I rigth? – Guerlando OCs Dec 09 '19 at 00:36
  • 1
    Does this answer your question? [why does std::condition\_variable::wait need mutex?](https://stackoverflow.com/questions/46088363/why-does-stdcondition-variablewait-need-mutex) – walnut Dec 09 '19 at 00:43

2 Answers2

5

Condition variables are stateless. They do not have a "signaled" state or any state of their own.

To wait for something, you need state. You need to know whether the thing you're waiting for has happened (and thus you don't need to wait or can stop waiting) or not (in which case you need to wait). Without some state, there is no way you can decide whether or not to wait.

Again, the condition variable is stateless. So the state is not in the condition variable.

And the state needs to be shared. It needs to be accessed by the thread that is waiting for the thing to happen (to decide whether to wait and when to stop waiting) and the thread that indicates that the thing happened (so it can tell the other thread not to wait or to stop waiting). Thus, it must be synchronized somehow.

Now, you have a bit of a problem. Consider:

  1. You acquire the lock that protects the shared state.
  2. The thing you're waiting for hasn't happened yet.
  3. You wait.

Oops, the other thread can't change the shared state since you hold a lock on it. Let's try it again.

Maybe:

  1. You acquire the lock that protects the shared state.
  2. The thing you're waiting for hasn't happened yet.
  3. You release the lock.
  4. You wait.

Oops. What if after step 3 but before step 4, another thread acquires the lock, changes the shared state, signals the condition variable, and releases the lock. You're now waiting for something that already happened. (Remember, the condition variable is stateless and doesn't change to a "signaled" state.)

To fix this, a condition variable provides an atomic "unlock and wait" operation that does steps 3 and 4 atomically. This only works if the mutex associated with the condition variable protects the shared state.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
  • I understand that, in the case of a shared state, a lock must be used. However, `wait` can be called without a state at all. It just waits for a notification. – Guerlando OCs Dec 09 '19 at 04:24
  • @GuerlandoOCs You cannot use condition variables that way. It might just happen to work, but it also might fail on a different platform. It will not work reliably without state because there is no way for the thread to make a sane decision whether to wait or not. [This](https://stackoverflow.com/questions/8594591/why-does-pthread-cond-wait-have-spurious-wakeups) may or may not help to clear this up, but it gets quite complicated. – David Schwartz Dec 09 '19 at 04:41
2

std::condition_variable requires std::mutex with std::unique_lock because this is the most common use case. Main reason for this design is that normally people use std::condition_variable to wait till certain condition is met - and normally you need a mutex for this purpose.

Although, in my humble experience, the thread reawakens only once notified - it is not promised by the standard and probably it behaves differently on other platforms. The thread might reawaken unexpectedly due to OS stuff. wait without condition function is simply there to allow a more freedom of use of the std::condition_variable.

So, in your case when one thread triggers another - you still need to check whether the trigger actually occurred. So you need a shared bool or something for that.

At any rate, you can use std::condition_variable_any with any lockable - including fake trivial ones. Though, I do not recommend using fake locks.

parsley72
  • 8,449
  • 8
  • 65
  • 98
ALX23z
  • 4,456
  • 1
  • 11
  • 18