Another big topic of concurrent programming is how to synchronize the threads between each other. There are several techniques which allow us to do the synchronization. Today, we will study synchronization mechanism called a condition variable.
The condition variable is associated with an event or condition. Roughly speaking, a thread is doing some of the following activities
changing the condition and notifying other threads about it,
waiting for the condition to be satisfied.
Let’s clarify the abstractions above with an example.
Mother and son with the laundry
There is a family with a mother and a son. The son is playing basketball and he is sweating, because he is playing hard. Afterwards, he asks the mother to do the laundry for him. She is nice and does it. The son is happy, because he has fresh shirt and shorts. (Any connection with the author of this article is merely apparent :-).)
We will put this short story into the code. There are two processes:
the mother is doing the laundry,
the son is playing basketball and outsourcing the laundry to his mother.
These two processes will run in separate threads. There is a communication/synchronization between them which is done via a condition variable.
Let’s begin with the code. We start with include statements and global variables. We would avoid the global variables in serious code and use classes with data members. But it is OK to use global variables for this demonstration.
Because we will use a condition variable, we need to include
<condition_variable> header. The condition variable
CVis associated with two things:
MUT, which helps with the synchronization.
SONS_LAUNDRY, which describes the state of the laundry.
The next two functions check the state of the laundry and return true if the laundry is clean or dirty.
The next section of code describes the behavior of the mother.
The function acquires an unique lock on the mutex. Then it calls a
wait() member function, of the
passing the lock and the function
wait() function checks the condition, by calling
is_laundry_dirty(). There are two possibilities:
If the condition is satisfied – the laundry is dirty –
wait()returns and the program continues.
If the condition is not satisfied – the laundry is clean –
wait()unlocks the mutex and puts the thread in a waiting state. The thread stops waiting when a
notify_one()member function of the condition variable is called. When the thread stops waiting, it reacquires the lock of the mutex and checks the condition again, by calling
wait()returns only if
wait()starts waiting again.
We must use a
wait() unlocks the mutex and locks it again. This is not
possible with the
Once the condition is satisfied, we change the
Loundry::CLEAN. (The mother does the laundry.) Afterwards,
the function unlocks the mutex and notifies the condition variable by calling
notify_one(). The notification is aiming the other thread
The next section of code describes the behavior of the son.
The function prints some information and then locks the mutex. The mutex is
automatically unlocked, after we set the
dirty, because we put the lock guard object inside a new scope.
Then, the function notifies the waiting thread (the mother).
Afterwards, we start waiting for the clean laundry. The code is again inside a
new scope and it is similar to the beginning of the
clean_laundry() function. The difference is that we are
waiting for the clean laundry and not for the dirty one.
At the end, the printing indicates that the mother cleaned the laundry.
The main function is simple. It creates two threads:
son with functions
You can look at the entire source code here.
Running the example
The code might be a bit complicated at first sight. The figure can help us understand the timeline of the execution of the program.
The red color represents the
mother’s thread, the blue color
son’s thread and the black color in the middle
represents condition variable.
mother starts with waiting
for the dirty laundry, while the
son is playing
son, via the condition variable, asks
mother to do the laundry for him. The
son is then waiting for the clean laundry while the
mother does the laundry.
mother, via the condition variable, informs the
son about the clean laundry and the
lives happily ever after.
We can also verify the code by running it and looking at the output.
We learned one of the techniques for thread synchronization.