In this article, we will see how to synchronize two threads using the mutex and conditional variables.
We will use the following APIs from the pthread library
- pthread_create() – To create a new thread
- pthread_mutex_lock() – To lock the critical section
- pthread_mutex_unlock() – To unlock the access to the critical section
- pthread_cond_wait() – Block the thread on a cond
- pthread_cond_signal() – Unblock the thread waiting on a cond
We will be writing a simple example program that has two threads in which one thread prints an even number and the other thread prints an odd number in a synchronization manner.
#include <stdio.h>
#include <pthread.h>
pthread_attr_t t1_attr, t2_attr;
pthread_t thread_id1, thread_id2;
int counter = 1;
pthread_mutex_t mutex;
pthread_cond_t even_cond, odd_cond;
void* odd() {
printf("odd_thread\n");
while (counter <= 10) {
pthread_mutex_lock(&mutex);
if (counter % 2 == 0) {
pthread_cond_wait(&even_cond, &mutex);
}
else {
printf("odd\n");
printf("%d\n", counter);
counter = counter + 1;
pthread_cond_signal(&odd_cond);
pthread_cond_wait(&even_cond, &mutex);
}
pthread_mutex_unlock(&mutex);
}
}
void* even(void* arg) {
printf("even thread\n");
while (counter <= 10) {
pthread_mutex_lock(&mutex);
if (counter % 2 != 0) {
pthread_cond_wait(&odd_cond, &mutex);
}
else {
printf("even\n");
printf("%d\n", counter);
counter = counter + 1;
pthread_cond_signal(&even_cond);
pthread_cond_wait(&odd_cond, &mutex);
}
pthread_mutex_unlock(&mutex);
}
}
int main()
{
pthread_attr_init(&t1_attr);
pthread_attr_init(&t2_attr);
pthread_create(&thread_id1, &t1_attr, even, NULL);
pthread_create(&thread_id2, &t2_attr, odd, NULL);
pthread_exit();
return 0;
}
Output
$ gcc -pthread -o thread thread.c
$ ./thread
odd_thread
odd
1
even thread
even
2
odd
3
even
4
odd
5
even
6
odd
7
even
8
odd
9
even
10
Explanation
While creating a thread we have to pass the thread attributes which include scheduling policy, scheduling priority, stack size, guard size, etc. All of these attributes can be initialized by using the pthread_attr function.
After initializing the thread we use the pthread_create function to create the thread.
When the main process terminates, automatically the threads associated with it will also get terminated. So, to prevent that we used the pthread_exit() function which prevents the main process to wait for the thread termination.
The logic which we implemented is pretty straightforward. After two threads are created either of the thread will get scheduled.
For example, if an even thread gets scheduled at first then it will enter into the if condition because initially, the counter has an odd value i.e 1 and it will be kept in a wait state by the pthread_cond_wait(odd_cond) and mutex will be released.
As the event thread is in a wait state, now, the odd thread will get scheduled and it will enter into the else block and lock the mutex and then increment the counter to 2 (even). After that, it will give a signal to unblock the even thread using pthread_signal(), and then it will enter into a wait state.
Likewise, after the even thread completes its task then it will enter into a wait state and give a signal to the odd thread and the same thing continues till the end.