Condition variables (or condvars) are remarkably similar to the sleepon locks we just saw above. In fact, sleepon locks are built on top of condvars, which is why we had a state of CONDVAR in the explanation table for the sleepon example. It bears repeating that the pthread_cond_wait() function releases the mutex, waits, and then reacquires the mutex, just like the pthread_sleepon_wait() function did.
Let's skip the preliminaries and redo the example of the producer and consumer from the sleepon section, using condvars instead. Then we'll discuss the calls.
/* * cp1.c */ #include <stdio.h> #include <pthread.h> int data_ready = 0; pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t condvar = PTHREAD_COND_INITIALIZER; void * consumer (void *notused) { printf ("In consumer thread...\n"); while (1) { pthread_mutex_lock (&mutex); while (!data_ready) { pthread_cond_wait (&condvar, &mutex); } // process data printf ("consumer: got data from producer\n"); data_ready = 0; pthread_cond_signal (&condvar); pthread_mutex_unlock (&mutex); } } void * producer (void *notused) { printf ("In producer thread...\n"); while (1) { // get data from hardware // we'll simulate this with a sleep (1) sleep (1); printf ("producer: got data from h/w\n"); pthread_mutex_lock (&mutex); while (data_ready) { pthread_cond_wait (&condvar, &mutex); } data_ready = 1; pthread_cond_signal (&condvar); pthread_mutex_unlock (&mutex); } } main () { printf ("Starting consumer/producer example...\n"); // create the producer and consumer threads pthread_create (NULL, NULL, producer, NULL); pthread_create (NULL, NULL, consumer, NULL); // let the threads run for a bit sleep (20); }
Pretty much identical to the sleepon example we just saw, with a few variations (we also added some printf() functions and a main() so that the program would run!). Right away, the first thing that we see is a new data type: pthread_cond_t. This is simply the declaration of the condition variable; we've called ours condvar.
Next thing we notice is that the structure of the consumer is identical to that of the consumer in the previous sleepon example. We've replaced the pthread_sleepon_lock() and pthread_sleepon_unlock() with the standard mutex versions (pthread_mutex_lock() and pthread_mutex_unlock()). The pthread_sleepon_wait() was replaced with pthread_cond_wait(). The main difference is that the sleepon library has a mutex buried deep within it, whereas when we use condvars, we explicitly pass the mutex. We get a lot more flexibility this way.
Finally, we notice that we've got pthread_cond_signal() instead of pthread_sleepon_signal() (again with the mutex passed explicitly).