Hello and welcome!
In this video session we will discuss on the condition variables, condition variables or a type of
signaling and waiting mechanism.
A condition variable allows signaling from one thread to the other thread, and the the other thread
keeps waiting for the signal to arrive.
And once the signal arrives, the the other thread takes the appropriate action.
So in the previous video we saw the mutex lock.
Also, the sequence of execution of threads is not in user's control because the threads can be scheduled
in any manner.
But when we have some use case where a proper order of execution of threads has to be followed.
For example, let us see an example of a producer and consumer where first producer thread needs to
be executed, and the output of the producer thread needs to be consumed by the consumer thread.
So in this case, the producer thread needs to be executed first, and then the consumer thread needs
to be executed after the execution of the producer thread.
In this case, we can make use of a condition variable where the producer thread makes use of the signaling
mechanism once the data or the buffer is being produced.
It signals the change and the consumer thread waits until the signal is being received.
Once the signal is received, the consumer thread uses the buffer of the producer thread.
Also, a condition variable is always used in conjunction with the mutex, so whenever the consumer
thread is waiting on the signal.
It it sleeps or blocks until the signal is received.
So let's see the statically allocated condition variable.
So condition variable.
The data type of the condition variable is p thread underscore second underscore t.
And we have to specify the value of p thread underscore second underscore initializer initializer.
So this is the static condition variable.
So we have two types of condition variables that is statically allocated and the dynamically.
So we will be discussing on static condition variables.
So the main principle behind condition variable is signal.
And wait the the signal operation is performed by the producer.
And it is kind of a notification to one or more of the threads saying that the the shared variables
state has been changed.
And now the other thread can access the shared resource.
The wait operation is a method of blocking until the signal is being received from the other thread.
Let's see the syntax of condition variable.
So we have two types of operations.
One is the signaling operation and the other is the wait operation.
This function p thread condition signal is used to signal to the other thread and p thread condition.
Wait is the function which is being used by the thread that needs to be blocked until the change of
the global variable occurs.
So we need to include the PCH and the syntax is p thread condition signal.
And it takes the condition variable and p thread condition wait.
And the p thread condition wait takes the condition variable along with the mutex.
Let's discuss why we have to use the condition variable along with the mutex.
So on.
Success.
It returns a zero or a positive number if there is an error.
Now let's see the working of the condition.
Wait.
The thread condition.
Wait.
Function call.
So in this case we have a condition variable cond and a mutex.
So whenever a condition variable is called by the thread this is usually called by the thread which
is waiting for the change of signal that is occurred by the other thread.
In this case, the first step would be the thread calling this.
The thread condition wait unlocks the mutex m, so whenever the mutex is locked, first thing will be
all the unlocking of the mutex.
Then the second thing will be blocking of the condition variable on second until it receives a signal
from the other thread.
So step number one.
And step number two is performed in an atomic way.
This means.
When.
When this p thread condition wait is performed, the step one and two is performed in such a way that
no other threads can interrupt the execution of the p thread condition.
Wait.
Once the function waits, blocks for the condition variable.
Now it waits for the signal, and as soon as it receives the required signal, it again locks the mutex
m specified in this.
So we can see the example in order to understand the working of condition variable.
So in this case let's open the program.
So here we are including the required header files.
And as we can see we have two types of variable.
First is the mutex And the second is the condition variable.
So the mutex we have already discussed in the previous video session.
And this is the variable of type p thread underscore second underscore t.
That means this is a condition variable.
And both are used in a statically manner static way.
So for mutex we need to specify pthread mutex initializer.
And for the condition variable we need to specify p thread cond initializer.
So both are a static initializer.
Now.
Now we have a flag to say to signal the change of the state.
And let's say we have a global buffer variable.
And here let's begin with the main thread the main thread and the main thread in this case creates a
p thread a and p thread b and it The thread A and thread B starts execution from its function.
That is, thread a and thread B, and the main thread waits for both the threads to complete the execution.
Now let's go to the definition of thread a.
In this case, thread A is the producer thread, and thread B is the consumer thread.
In this case, thread B has to execute its critical section only after the producer thread has produced
the data.
So here our use case is in such a way that even though we don't have the control on the scheduling of
the thread, we have to make sure that the sequence of execution is as per the requirement.
So in this case, first the producer thread has to produce some data in the buffer.
Then after producing the data then only the consumer thread has to read the buffer data.
So now let's see how to achieve this.
As mentioned we need both the mutex variable and the condition variable.
In this case let's say that the producer thread that is thread A is executing and the producer thread
puts a lock on my mutex variable.
And then as soon as it puts a lock it tries to fill the buffer with some required data.
And meanwhile before this the value of done flag will be zero in this case.
So before executing of any of the thread the flag value will be zero.
Now let's come to the consumer thread.
In consumer thread we have to put a lock Then we have to check on the condition variable.
And this is the flag we have to check on.
And we have to wait for the condition variable by specifying condition variable and the mutex.
So we have to specify both the condition variable as well as the mutex.
So now let's go back to the working of this function p thread condition.
Wait.
So now what we had discussed is the first step would be it unlocks the mutex specified here.
So when this function is called it first unlocks this mutex.
So in order to unlock this we have to call a lock of this mutex before it unlocks.
So the locked mutex here will be unlocked here.
And it waits for this condition variable.
So this unlocking and locking is done in an atomic Manner.
Now the lock is being unlocked and the producer thread can now get the lock and.
And and produce the required data.
And now once the required data has been produced, it can it can change the state of the variable to
one saying that it has now produced the required data.
And then it sends a signal on my Co wire, which is nothing but a condition variable.
And and then it unlocks.
So once the producer sends a signal here it unlocks this statement.
Because by this statement it is waiting for a signal on this variable.
And this signal is being sent from the producer thread now.
And once the signal is received here now the next step would be to lock the mutex.
So now as soon as the signal is received it locks this mutex.
Now the mutex owner is thread b and then the thread b consumes the shared buffer.
And then finally after consuming the buffer it unlocks.
Let's compile this once and see the result.
Now let's run the program.
Now we can see that in this we are printing the critical section regions where it is required.
So this is the critical section the in the critical section we are locking.
And then unlocking.
So this part is the critical section.
And in this case we are.
This part is a critical section here.
So if you see here the scheduling of the thread is not in our control.
It can schedule in any manner but critical section execution of thread A occurs first, then the signal
is received from thread A, and this statement is always executed after the thread A executes.
So we can see that in thread B, we are reading the buffer that is produced by thread A.
Now, even if we run for any number of times, we will get that thread.
A critical section is first executed, then only the thread B critical section is executed.
So any number of times we run we can see that thread A is critical section will be executed first.
If we observe in this thread, B is scheduled first before thread A has scheduled, even though thread
B is scheduled.
We are making sure that the critical section first executes thread A, then only the thread B critical
section executes.
So here we are maintaining the sequencing of critical section execution.
If we go back let's go back to the program here.
Now let's try to analyze this in other way.
Now let's say that let's consider the case where the thread A has scheduled first.
So when thread A has scheduled first now thread A has got the lock, and when thread A has got the lock,
it now puts the data to the buffer.
So meanwhile the done flag is still equal to zero here.
Before this statement the done flag equal to zero.
Now let's assume that thread B has scheduled and it has put a lock, but when it is trying to put a
lock, the lock owner is already threaded.
So thread B blocks here and now the done flag is one and now the condition variable is set and it unlocks.
As soon as it unlocks the thread, B gets the lock and then it unlocks this.
And since the signal is already received, it locks again and consumes the global variable.
Now let's assume that first the scheduling is in thread B.
So now let's say that the thread B has put a lock initially on my mutex and now done flag is equal to
zero.
Now this done flag is equal to zero.
So it executes this line.
So here by executing this line.
The owner of lock was thread B.
Now it unlocks this thread unlocks this lock.
And now waits for the signal on this condition variable.
Now as soon as the lock is been unlocked by thread B, the thread A when it is scheduled can get the
lock.
And now the owner of the lock is ready.
And now.
Now the thread A produces the data and it signals on condition variable and then unlocks the data as
soon as it unlocks and sends the signal.
The thread B, which is waiting for the signal, receives the signal, and now it locks the locks the
mutex, and then it performs the consuming of the buffer.
And then it further unlocks the mutex.
By this, we can make sure that the producer and consumer thread is following a sequence of execution.