There is one type of synchronization that exists between threads called locks. In this case, thread needs mutual exclusion. It requires one thread to execute at a time. Then, also one another common pattern required when multiple threads are interacted. They are – waiting & signalling. One thread will wait until other thread finishes its job. After completing the execution of this thread, the waiting thread can execute in the processor. For such kind of thread synchronization, we need another technique, called busy waiting on some variables. For this, we need a new synchronization primitive called condition variables.
This thread concept is available in different operating system. But we will go to the name, condition variables which is applied as pthread library in Linux operating system
The function pthread_cond_wait () is a component of wait. This function by default performs the unlocking action.
Programming example-1:
In this programming example we will see how this function executes:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER ;// declaring the condition variable.
pthread_cond_t c = PTHREAD_COND_INITIALIZER ;
int done = 0 ;
void thr_exit ()
{
pthread_mutex_lock(&m) ;
done = 1 ;
pthread_cond_signal(&c) ;
pthread_mutex_unlock(&m) ;
}
void *child(void *arg) // child process executes.
{
printf ( " child \n " );
thr_exit();
return NULL ;
}
void thr_join()
{
pthread_mutex_lock(&m) ;
while( done == 0 )
pthread_cond_wait(&c ,&m) ; // call the function inside the thr_join () function.
pthread_mutex_unlock(&m) ;
}
int main(int argc, char* argv[]) {
printf ( " parent: begin \n " ) ;
pthread_tp ;
pthread_create(&p, NULL, child, NULL ) ; // Initializing the thread.
thr_join() ;
printf ( " parent: end \n " ) ;
return 0 ;
}
Output:
Explanation
In this programming example there exist two processes called a parent thread & a child thread. Here, parent wants to wait for the child. After completing the execution of child process, parent starts its execution.
For the inside the main () we create the parent and child thread. Child thread executes the function named child (). Then parent thread calls the function join. This join () basically checks that the child is done. Otherwise it will wait by using pthread_cond_wait () function. When the child runs, it calls the exit (). When the child’s execution is done, the value of variable done is going to be 1. Then, it calls the signal on condition variable. After that the execution of parent thread will start.
Wait and Signal syntax:
{
done = 1 ;
pthread_cond_signal ( &c ) ;
}
void thr_join ()
{
if ( done == 0 )
pthread_cond_wait ( &c ) ;
}
What happens if we don’t get a lock? It will occur a race condition – missed wakeup.
- parent process has nothing for execution and going to idle condition, for that case interruption may be occur.
- When child executes its code and accomplished its execution, then it signals but no one sleeping yet.
- parent now restarted its execution from where it finished off and has to be waited for a specific period.
- For this wait function has to keep in its mind that thread is not suspended for a longer period of time. It has to be opened its lock.
Programming example-2:
In this programming example we will see another use of pthread_cond_wait fuction.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
pthread_mutex_tmutexFuel ;
pthread_cond_tcondFuel ; // creating the condition variable.
int fuel = 0;
void* fuelling(void* arg) {
for (int i = 0; i < 5; i++) {
pthread_mutex_lock(&mutexFuel);
fuel += 15 ;
printf ( " Got fuel... %d \n " , fuel ) ;
pthread_mutex_unlock(&mutexFuel) ;
pthread_cond_signal(&condFuel) ;
sleep(1);
}
}
void* vehicle(void* arg) {
pthread_mutex_lock(&mutexFuel);
while (fuel < 40) {
printf ( " Zero fuel. Waiting... \n " ) ;
pthread_cond_wait(&condFuel, &mutexFuel);
// Equivalent to:
// pthread_mutex_unlock(&mutexFuel);
// wait for signal on condFuel
// pthread_mutex_lock(&mutexFuel) ;
}
fuel -= 40 ;
printf ( " fuel collected Now left: %d \n " , fuel ) ;
pthread_mutex_unlock(&mutexFuel);
}
int main(int argc, char* argv[]) {
pthread_ta[2] ;
pthread_mutex_init(&mutexFuel, NULL) ;
pthread_cond_init(&condFuel, NULL) ;
for ( int i = 0 ; i < 2 ; i++ ) {
if (i == 1) {
if (pthread_create(&a[i], NULL, &fuelling, NULL) != 0) {
perror("Failed to create thread");
}
} else {
if (pthread_create(&a[i], NULL, &vehicle, NULL) != 0) {
perror("Failed to create thread");
}
}
}
for ( int i = 0 ; i < 2 ; i++ ) {
if (pthread_join(a[i], NULL) != 0) {
perror("Failed to join thread") ;
}
}
pthread_mutex_destroy(&mutexFuel) ;
pthread_cond_destroy(&condFuel) ; // destroying the threads.
return 0 ;
}
Output:
Explanation
Here, we will see the application of pthread_cond_wait () function. It is like when someone wants to fill the fuel his car. He has to wait until his car will be filled up with fuel. For this, we create two threads. One for filling the fuel in the car and for the car. We will define two functions. One is fuelling () function which determines when to fill the car with fuel and another is vehicle () function to get fuel. The guy who wants to fill the fuel in his car has to wait until the fuel is filled up.
Inside the main () function we will create two threads for fuelling () and for vehicle () function. Now, we will create a condition variable to wait for condition. For this we will see the output. When functions complete their execution, we will destroy these threads.
Conclusion
The pthread_cond_wait () function notifies when we have done some program with the help of multiple threads. This function will make some condition variables to handle these threads. Actually, the characteristics of this function totally depends on the principle of wait and signal theory.