views:

393

answers:

6

I'm trying to learn about condition variables. I would like to know what are the common situations where condition variables are used.

One example is in a blocking queue, where two threads access the queue - the producer thread pushes an item into the queue, while the consumer thread pops an item from the queue. If the queue is empty, the consumer thread is waiting until a signal is sent by the producer thread.

What are other design situations where you need a condition variable to be used?

I'd prefer examples based from experience though, such as those in real live applications.

+2  A: 

Monitor (synchronization) - Wikipedia, the free encyclopedia

http://en.wikipedia.org/wiki/Monitor_(synchronization)

POSIX Threads Programming

https://computing.llnl.gov/tutorials/pthreads/

try this one

Linux Tutorial: POSIX Threads

www.yolinux.com/.../LinuxTutorialPosixThreads.html

ratty
Thanks but I've actually gone through them previously. I prefer examples based on actual applications or based from experience though.
jasonline
+1  A: 

An example, in addition to the consumer-producer model, which you already mentioned is the use in barrier synchronization. When threads enter the barrier, if there are still other threads that need to enter the barrier, then they wait on a condition variable. The last thread to enter the barrier signals the condition.

Michael Aaron Safyan
@Michael: I'm not really familiar with barriers... but in your example, are you saying you used barriers with a separate condition variable? I thought you can implement the above situation using barriers alone and not need a separate condition variable?
jasonline
@jasonline, so, you can use the barriers provided by pthreads, but you can also implement your own barrier using a mutex and a condition variable (which is how pthreads most likely provides their own). When you enter the barrier, you lock the mutex, increment the count, and store the barrier sense. If the count has reached the maximum, you reset the count to zero, reverse the barrier sense, signal the condition, and exit the barrier. Otherwise, you wait on the condition, until the barrier sense is the opposite of the one stored.
Michael Aaron Safyan
@Michael: Ah, yes. I get your point. Thanks.
jasonline
A: 

One use of condition variables that's a bit more complicated than just a message queue, is to "share a lock", where different threads are waiting for subtly different conditions of the same basic nature. For instance, you have a (very shonky, simplified) web cache. Each entry in the cache has three possible states: not present, IN_PROGRESS, COMPLETE.

getURL:
    lock the cache
    three cases for the key:
        not present:
            add it (IN_PROGRESS)
            release the lock
            fetch the URL
            take the lock
            update to COMPLETE and store the data
            broadcast the condition variable
            goto COMPLETE
        COMPLETE:
            release the lock and return the data
        IN_PROGRESS:
            while (still IN_PROGRESS):
                wait on the condition variable
            goto COMPLETE

I have in practice used the pattern to implement a variant of the POSIX function pthread_once without any help from the scheduler. The reason I couldn't use a semaphore or lock per once_control, and just do the initialization under the lock, is that the function wasn't allowed to fail, and the once_control had only trivial initialization. For that matter, pthread_once itself has no defined error codes, so implementing it to possibly fail doesn't leave your caller with any good options...

Of course with this pattern you have to be careful about scaling. Each time any initialization is completed, every waiting thread wakes up to grab the lock. So when you design the system you think very carefully about sharding, and then decide you can't be bothered doing anything to actually implement it until you see proven performance problems.

Steve Jessop
A: 

I used it to send synchronized messages, where a sync-object was added.
The sync object consisted of a condition variable with a "ready" boolean.
In the syncMsg::send() funtion, there was a sync->wait() and in the syncMsg::handle() function, there was a sync->go().

Should be used judiciously because of possible deadlocks.

stefaanv
A: 

I use condition variables instead of the error-prone Win32 Event objects. With condvars, you don't have to worry so much about spurious signaling. It is also easier to wait for multiple events to occur.

Condvars can also replace semaphores, because they are more general-purpose.

Emile Cormier
A: 

I know this is not very helpful but I use condition variable any time I want a thread to wait for something to happen, or only wait until something happens.

A very common pattern for where I use a condition variable is a background thread that wakes up every few minutes to do some processing then goes back to sleep. At shutdown the main thread to signals the background thread to finish and then join it finishing. The background thread waits on the condition with a timeout, to perform its sleep.

The background thread follows this basic logic

void threadFunction() {
    initialisation();

    while(! shutdown()) {
        backgroundTask();

        shutdown_condition_wait(timeout_value);
    }

    cleanup();
}

This lets the background thread shutdown promptly and gracefully.

If I have a number of such threads the main function signals each to shutdown then joins each one after the next. This enables each thread component to shutdown in parallel.

iain