How to Wait on Multiple Condition Variables in C++11

What is the best way to wait on multiple condition variables in C++11?

You ask,

What is the best way to wait on multiple condition variables in C++11?

You can't, and must redesign. One thread may wait on only one condition variable (and its associated mutex) at a time. In this regard the Windows facilities for synchronization are rather richer than those of the "POSIX-style" family of synchronization primitives.

The typical approach with thread-safe queues is to enqueue a special "all done!" message, or to design a "breakable" (or "shutdown-able") queue. In the latter case, the queue's internal condition variable then protects a complex predicate: either an item is available or the queue has been broken.

In a comment you observe that

a notify_all() will have no effect if there is no one waiting

That's true but probably not relevant. wait()ing on a condition variable also implies checking a predicate, and checking it before actually blocking for a notification. So, a worker thread busy processing a queue item that "misses" a notify_all() will see, the next time it inspects the queue condition, that the predicate (a new item is available, or, the queue is all done) has changed.

waiting for multiple condition variables in boost?

I don't believe you can do anything like this with boost::thread. Perhaps because POSIX condition variables don't allow this type of construct. Of course, Windows has WaitForMultipleObjects as aJ posted, which could be a solution if you're willing to restrict your code to Windows synchronization primitives.

Another option would to use fewer condition variables: just have 1 condition variable that you fire when anything "interesting" happens. Then, any time you want to wait, you run a loop that checks to see if your particular situation of interest has come up, and if not, go back to waiting on the condition variable. You should be waiting on those condition variables in such a loop anyways, as condition variable waits are subject to spurious wakeups (from boost::thread docs, emphasis mine):

void wait(boost::unique_lock<boost::mutex>& lock)

...

Effects:

Atomically call lock.unlock() and blocks the current thread. The thread will unblock when notified by a call to this->notify_one() or this->notify_all(), or spuriously. ...

How to make a thread wait for multiple conditional signals from different threads?

Does the pthread library (in C/C++) have a function like pthread_cond_wait(&cond, &mutex) that can wait for conditional signals (such as pthread_cond_signal(&cond1), pthread_cond_signal(&cond2) and so on...) from multiple different threads?

What you're describing is not about different threads -- pthread_cond_wait() / pthread_cond_signal() already handle that just fine, as indeed they need to do to serve their intended purpose. If a given thread is waiting on condition variable cond1 then it can be awakened by any thread that signals or broadcasts to cond1. And ultimately, that may be the direction you want to go.

But what you actually asked about is whether a thread can block on multiple condition variables at the same time, and no, pthreads makes no provision for that. Nor would that make sense for the usage model for which condition variables are designed.

I suspect that you are looking at CV waiting / signaling as purely a notification mechanism, but this is altogether the wrong view. A thread uses a condition variable to suspend execution until another thread performs work that causes some condition to be satisfied (otherwise, why wait at all)? That's normally a condition that can be evaluated based on data protected by the associated mutex, so that the prospective waiting thread can

  1. avoid data races involving the data in question (by locking the mutex at the beginning, before accessing them, and relying on other threads to do the same);
  2. be assured (because it holds the mutex locked) that the condition will not change unexpectedly after it is evaluated;
  3. avoid waiting at all when the condition is already satisfied;
  4. verify when it resumes from waiting that the condition in fact is satisfied (MANDATORY).

When the condition indeed is satisfied, the thread moves on with whatever action it wanted to perform that required the condition to be satisfied. That's important. The thread waited to be able to perform a specific thing, so what does it matter if the conditions are then right for performing some other thing, too? And if it wants to perform that other thing next, then it can do it without waiting if the conditions for that still hold.

On the side of the signal sender, you should not think about it as notifying a specific thread. Instead, it is announcing to any thread that cares that something it is presently interested in may have changed.

I appreciate that this is all fairly abstract. Multithreaded programming is challenging. But this is why students are given exercises such as producer / consumer problems or the Dining Philosophers problem. Learn and apply the correct idioms for CV usage. Think about what they are achieving for you. It will become clearer in time.

How can I wait on multiple things

If I understand your problem correctly, I would probably do something like this:

 bool WaitAndPop(T& value)
{
std::unique_lock<std::mutex> lk(mutex);

// Wait until the queue won't be empty OR stop is signaled
condition.wait(lk, [&] ()
{
return (stop || !(myQueue.empty()));
});

// Stop was signaled, let's return false
if (stop) { return false; }

// An item was pushed into the queue, let's pop it and return true
value = myQueue.front();
myQueue.pop_front();

return true;
}

Here, stop is a global variable like condition and myQueue (I suggest not to use queue as a variable name, since it is also the name of a Standard container adapter). The controlling thread can set stop to true (while holding a lock to mutex) and invoke notifyOne() or notifyAll() on condition.

This way, notify***() on the condition variable is invoked both when a new item is pushed into the queue and when the stop signal is being raised, meaning that a thread waking up after waiting on that condition variable will have to check for what reason it has been awaken and act accordingly.

Wait on multiple condition variables on Linux without unnecessary sleeps?

If you are talking about POSIX threads I'd recommend to use single condition variable and number of event flags or something alike. The idea is to use peer condvar mutex to guard event notifications. You anyway need to check for event after cond_wait() exit. Here is my old enough code to illustrate this from my training (yes, I checked that it runs, but please note it was prepared some time ago and in a hurry for newcomers).

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>

static pthread_cond_t var;
static pthread_mutex_t mtx;

unsigned event_flags = 0;
#define FLAG_EVENT_1 1
#define FLAG_EVENT_2 2

void signal_1()
{
pthread_mutex_lock(&mtx);
event_flags |= FLAG_EVENT_1;
pthread_cond_signal(&var);
pthread_mutex_unlock(&mtx);
}

void signal_2()
{
pthread_mutex_lock(&mtx);
event_flags |= FLAG_EVENT_2;
pthread_cond_signal(&var);
pthread_mutex_unlock(&mtx);
}

void* handler(void*)
{
// Mutex is unlocked only when we wait or process received events.
pthread_mutex_lock(&mtx);

// Here should be race-condition prevention in real code.

while(1)
{
if (event_flags)
{
unsigned copy = event_flags;

// We unlock mutex while we are processing received events.
pthread_mutex_unlock(&mtx);

if (copy & FLAG_EVENT_1)
{
printf("EVENT 1\n");
copy ^= FLAG_EVENT_1;
}

if (copy & FLAG_EVENT_2)
{
printf("EVENT 2\n");
copy ^= FLAG_EVENT_2;

// And let EVENT 2 to be 'quit' signal.
// In this case for consistency we break with locked mutex.
pthread_mutex_lock(&mtx);
break;
}

// Note we should have mutex locked at the iteration end.
pthread_mutex_lock(&mtx);
}
else
{
// Mutex is locked. It is unlocked while we are waiting.
pthread_cond_wait(&var, &mtx);
// Mutex is locked.
}
}

// ... as we are dying.
pthread_mutex_unlock(&mtx);
}

int main()
{
pthread_mutex_init(&mtx, NULL);
pthread_cond_init(&var, NULL);

pthread_t id;
pthread_create(&id, NULL, handler, NULL);
sleep(1);

signal_1();
sleep(1);
signal_1();
sleep(1);
signal_2();
sleep(1);

pthread_join(id, NULL);
return 0;
}

Can multiple threads wait on the same condition variable?

This is undefined behavior.

In order to wait on a condition variable, the condition variable must be waited on by the same exact thread that originally locked the mutex. You cannot lock the mutex in one execution thread, and then wait on the condition variable in another thread.

auto lock = std::unique_lock(m);

This lock is obtained in the main execution thread. Afterwards, the main execution thread creates all these multiple execution threads. Each one of these execution threads executes the following:

       cv.wait(lock)

The mutex lock was not acquired by the execution thread that calls wait() here, therefore this is undefined behavior.

A more closer look at what you are attempting to do here suggests that you will likely get your intended results if you simply move

auto lock = std::unique_lock(m);

inside the lambda that gets executed by each new execution thread.

You also need to simply use notify_all() instead of calling notify_one() multiple times, due to various race conditions. Remember that wait() automatically unlocks the mutex and waits on the condition variable, and wait() returns only after the thread successfully relocked the mutex after being notified by the condition variable.

C++11 Thread: Multiple threads waiting on a condition variable

std::condition_variable does not specify which waiting thread is woken when you call notify_one. You should therefore write code that doesn't care which thread is woken. The standard pattern is that whichever thread is woken, that thread should do the work that needs to be done.

If you require that the threads are woken in a specific order, then use a different mechanism. You could, for example, have a separate std::condition_variable for each thread, and then put the threads in a queue when they need tools. As a thread hands in the tools, it could then signal the condition variable corresponding to the thread at the front of the queue. That thread will then be woken, and the others will remain sleeping (modulo spurious wake-ups).

Multiple Condition variable calling each other function

When I try to push the first element(push_front). " push_front()" function reaches condition variable and and goes to wait until it receives a notification [...]

This is false. Read the documentation of this API carefully. What you describe is the behavior of

template<typename L> void wait(L & lock);

which is number 3 in the list of public member functions. However, the call to wait in your code has two arguments; your code calls

template<typename L, typename Pr> void wait(L & lock, Pr pred);

which is number 4 in that list. This function is documented as being the same as

while ( !pred() )
wait(lock); // <-- This is where the thread waits for a notification

That is, the predicate is checked first, and only if it is false will the thread wait until a notification is received. As long as an empty buffer is not full, the predicate will be initially true, so execution proceeds without waiting for a notification. (However, if you were to construct a buffer with a capacity of zero, then this does look like it will hang when trying to push the first element into the buffer.)

multiple condition variables in c

There's really nothing tricky to it: presuming for one event you have code in thread A like:

pthread_mutex_lock(&lock);
while (!event_b_pending)
pthread_cond_wait(&cond, &lock);

/* Process Event B */

with code in thread B like:

pthread_mutex_lock(&lock);
event_b_pending = 1;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&lock);

Then for three events, you would change thread A to:

pthread_mutex_lock(&lock);
while (!event_b_pending && !event_c_pending && !event_d_pending)
pthread_cond_wait(&cond, &lock);

if (event_b_pending)
{
/* Process Event B */
}

if (event_c_pending)
{
/* Process Event C */
}

if (event_d_pending)
{
/* Process Event D */
}

with threads C and D working like thread B (except setting the appropriate flag).



Related Topics



Leave a reply



Submit