What are common uses of condition variables in C++?
Asked Answered
O

5

16

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.

Odd answered 19/3, 2010 at 9:45 Comment(5)
Should this be community wiki?Odd
Since you aren't looking for a specific answer and just more general "answers", probably. But I'm more on the fence of this one, you may want to wait for the nay or yay from others.War
+1 Take those well-earned reputation points while you can for this good question :-)Fabio
@GMan: Yes, I'm looking for a list of answers actually, more like a compilation. So, I'll wait...Odd
Hey, may I know the reason why I got a downvote?Odd
A
2

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.

Anticyclone answered 19/3, 2010 at 11:32 Comment(0)
D
1

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.

Dissimulation answered 19/3, 2010 at 10:28 Comment(2)
@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?Odd
@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.Dissimulation
G
0

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.

Goren answered 19/3, 2010 at 9:45 Comment(0)
A
0

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() function, there was a sync->wait() and in the syncMsg::handle() function, there was a sync->go().

Should be used judiciously because of possible deadlocks.

Azzieb answered 19/3, 2010 at 12:3 Comment(0)
C
0

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.

Cleruchy answered 19/3, 2010 at 12:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.