Signal versus broadcast
In this section, we'll talk about the difference between the two condvar functions pthread_cond_signal() and pthread_cond_broadcast().
The short story is this: the signal
version will wake up only one thread.
So, if there were multiple threads blocked in the wait
function,
and a thread did the signal,
then only one of the threads would wake up.
Which one?
The highest priority one.
If there are two or more at the same priority, the ordering of wakeup is
indeterminate.
With the broadcast
version, all blocked threads will wake up.
It may seem wasteful to wake up all threads. On the other hand, it may seem sloppy to wake up only one (effectively random) thread.
Therefore, we should look at where it makes sense to use one over the other.
Obviously, if you have only one thread waiting, as we did in either version
of the consumer program, a signal
will do just fine—one thread will
wake up and, guess what, it'll be the only thread that's currently waiting.
In a multithreaded situation, we've got to ask: Why are these threads
waiting?
There are usually two possible answers:
- All the threads are considered equivalent and are effectively
forming a
pool
of available threads that are ready to handle some form of request.
Or:
- The threads are all unique and are each waiting for a very specific condition to occur.
/*
* cv1.c
*/
#include <stdio.h>
#include <pthread.h>
pthread_mutex_t mutex_data = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cv_data = PTHREAD_COND_INITIALIZER;
int data;
thread1 ()
{
for (;;) {
pthread_mutex_lock (&mutex_data);
while (data == 0) {
pthread_cond_wait (&cv_data, &mutex_data);
}
// do something
pthread_mutex_unlock (&mutex_data);
}
}
// thread2, thread3, etc have the identical code.
In this case, it really doesn't matter which thread gets the data, provided that one of them gets it and does something with it.
/*
* cv2.c
*/
#include <stdio.h>
#include <pthread.h>
pthread_mutex_t mutex_xy = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cv_xy = PTHREAD_COND_INITIALIZER;
int x, y;
int isprime (int);
thread1 ()
{
for (;;) {
pthread_mutex_lock (&mutex_xy);
while ((x > 7) && (y != 15)) {
pthread_cond_wait (&cv_xy, &mutex_xy);
}
// do something
pthread_mutex_unlock (&mutex_xy);
}
}
thread2 ()
{
for (;;) {
pthread_mutex_lock (&mutex_xy);
while (!isprime (x)) {
pthread_cond_wait (&cv_xy, &mutex_xy);
}
// do something
pthread_mutex_unlock (&mutex_xy);
}
}
thread3 ()
{
for (;;) {
pthread_mutex_lock (&mutex_xy);
while (x != y) {
pthread_cond_wait (&cv_xy, &mutex_xy);
}
// do something
pthread_mutex_unlock (&mutex_xy);
}
}
In these cases, waking up one thread isn't going to cut it! We must wake up all three threads and have each of them check to see if its predicate has been satisfied or not.
This nicely reflects the second case in our question above (Why are these threads waiting?
).
Since the threads are all waiting on different conditions (thread1() is waiting
for x to be less than or equal to 7 or y
to be 15, thread2() is waiting for x
to be a prime number, and thread3() is waiting for x to be
equal to y), we have no choice but to wake them all.