Sleepons have one principal advantage over condvars. Suppose that you want to synchronize many objects. With condvars, you'd typically associate one condvar per object. Therefore, if you had M objects, you'd most likely have M condvars. With sleepons, the underlying condvars (on top of which sleepons are implemented) are allocated dynamically as threads wait for a particular object. Therefore, using sleepons with M objects and N threads blocked, you'd have (at most) N condvars (instead of M).
However, condvars are more flexible than sleepons, because:
The first point might just be viewed as being argumentative. :-) The second point, however, is significant. When the mutex is buried in the library, this means that there can be only one per process—regardless of the number of threads in that process, or the number of different sets of data variables. This can be a very limiting factor, especially when you consider that you must use the one and only mutex to access any and all data variables that any thread in the process needs to touch!
A much better design is to use multiple mutexes, one for each data set, and explicitly combine them with condition variables as required. The true power and danger of this approach is that there is absolutely no compile- or runtime checking to make sure that you:
The easiest way around these problems is to have a good design and a design review, and also to borrow techniques from object-oriented programming (like having the mutex contained in a data structure, having routines to access the data structure, etc.). Of course, how much of one or both you apply depends not only on your personal style, but also on performance requirements.
The key points to remember when using condvars are:
Here's a picture:
One interesting note. Since there is no checking, you can do things like associate one set of variables with mutex ABC, and another set of variables with mutex DEF, while associating both sets of variables with condvar ABCDEF:
This is actually quite useful. Since the mutex is always to be used for access and testing, this implies that I have to choose the correct mutex whenever I want to look at a particular variable. Fair enough—if I'm examining variable C, I obviously need to lock mutex MutexABC. What if I changed variable E? Well, before I change it, I had to acquire the mutex MutexDEF. Then I changed it, and hit condvar CondvarABCDEF to tell others about the change. Shortly thereafter, I would release the mutex.
Now, consider what happens. Suddenly, I have a bunch of threads that had been waiting on CondvarABCDEF that now wake up (from their pthread_cond_wait()). The waiting function immediately attempts to reacquire the mutex. The critical point here is that there are two mutexes to acquire. This means that on an SMP system, two concurrent streams of threads can run, each examining what it considers to be independent variables, using independent mutexes. Cool, eh?