“The usage of Mutex is the method for preventing race situations that is most frequently used. Mutex, which refers to mutual exclusion, essentially gives us a deadbolt lock that stops several users from simultaneously accessing and modifying the crucial data. Whenever multiple or more processes employ the same process as their medium for finishing their processing, deadlock must be avoided. The main subject of today’s discussion will be using the Posix Mutex function to avoid deadlock with different methods with C language in Ubuntu 20.04 system. Before doing anything with your code snippets, we should install the “gcc” compiler.”
Example 01
We are going to start our first illustration of POSIX mutex functions with the needed headers. Without these headers, our script of C will not be operatable in the way we want it. The first stdio.h header is responsible for the utilization of regular inputs and outputs in the code. The standard “stdlib” header is responsible for the smooth use of standard library functions in the code snippet. The unistd.h header is a must to encounter issues related to Unicodes. Here comes the library to utilize string variables and functions related to string in C language, i.e., string.h.
Finally, the library is the “pthread.h” header which is a must when you want to use multithreading in your snippets. This library includes thread creation, thread joining, thread lock, thread unlock, and many other functions related to multithreading.
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
After casting off the necessary headers, we are going to initialize 2 threads using the “pthread_t” object from the thread’s library. The value “t” will be used as thread ID in the near future. Then, an integer variable “I” is initialized to 0, and variable “c” got declared for counting the threads. The mutex lock object “ml” has been generated using the pthread_mutex_t object. Here comes the thread function “T,” which is going to lock and unlock the thread. To lock the calling thread, we are using the pthread_mutex_lock() function using the “ml” lock while counting the thread number.
The printf() statements are here to tell us about the initialization and end of a specific thread. Within initialization and end, the thread function “T” is locked and cannot be accessed by other threads. In the end, the pthread_mutex_unlock() function is used to unlock the specific thread and give access to other threads.
The main() function starts from the “if” statement that has been checking out some condition. It uses the pthread_mutex_init() function by passing it “ml” lock object and NULL value to initialize the mutex lock. If the init() function’s returned value is not equal to 0, the printf() statement will display that the mutex lock was not initialized due to some issue. Meanwhile, the while() loop uses the condition for variable “I,” which should be less than 2. If it is less than 2, then the thread_create() function will be called by passing thread id “t” as pointer and pointer towards the “T” thread function. If the thread got created successfully, it would return “0” to the “E” variable.
In case if it returns other than 0, it will print an, i.e., using the strerror function and increment the value of a variable “I”. This function is responsible for calling the thread function T for two different threads. The pthread_join() function uses thread ID to join them with the main() function, and the pthread_mutex_destroy() function is here to destroy the lock after all the functions have been executed. The moving back to a main() message will be displayed at the end of this program after thread executions.
if(pthread_mutex_init(&ml, NULL) != 0) {
printf("Failed to Initialize Mutex Lock...\n");
return 1;
}
while(i<2) {
int E = pthread_create(&t[i], NULL, &T, NULL);
if(E != 0)
printf("Failed to create Thread: [%s]", strerror(E));
i++;
}
pthread_join(t[0], NULL);
pthread_join(t[1], NULL);
pthread_mutex_destroy(&ml);
printf("Hello! We are back in Main method..\n");
return 0;
}
We are compiling the test.c file with the “gcc” compiler and the option –lpthread for threads execution. The execution shows that the first thread got access to the “T” function and locked.
After the lock was released from the first thread, then access to the T function was given to the second thread, and it got locked and unlocked as well.
In the end, both threads got executed, and the main() function got back control.
Example 02
Here comes another way to demonstrate the use of POSIX mutex to lock and unlock the threads. Within this example, we don’t require the unistd.h header, so we are not going to use it. Else, all the headers are here, which we used in the above example. The pthread_mutex_t object is used to create a mutex lock “ml”. The main() method starts by initializing two threads, t1, and t2, by using the pthread_t object. It uses the pthread_create() function twice to create threads by calling functions T1 and T2 by passing them thread initialization IDs as pointers, i.e., t1 and t2. On thread creation, the T1 and T2 function got executed one after another. Each of these functions is called the Test() function by passing 2 string values as parameters.
The Test function is used here to lock and unlock specific threads that are calling it, i.e., t1 and t2. For locking the threads, the pthread_mutex_lock() function is used, and for unlocking, the pthread_mutex_unlock() function is used, i.e., both are utilizing the “ml” mutex lock. Within the lock and unlock process, the printf statements are used with 5-second sleep after each. The first printf() method will display the first argument passed to the Test function, and the second printf() method will display the second string argument passed to the Test function.
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
pthread_mutex_t ml;
void* Test(char* p, char* q) {
pthread_mutex_lock(&ml);
printf("%s...\n", p);
sleep(5);
printf("%s...\n", q);
sleep(5);
pthread_mutex_unlock(&ml);
}
void* T1(void* z) {
Test("Initiated", "1st Thread"); }
void* T2(void* z) {
Test("Initiated", "2nd Thread"); }
int main() {
pthread_t t1, t2;
pthread_create(&t1, NULL, &T1, NULL);
pthread_create(&t2, NULL, &T2, NULL);
while(1) {}
exit(0);
}
On compiling, it will show you a warning, but you can ignore it for a moment.
The execution of the compiled code shows that the first thread got initiated and got all the resources of the Test function.
After this, the lock got released, and the 2nd thread got the resources. After the second thread, we have to exit the execution forcefully.
Conclusion
The POSIX mutex concept is briefly explained to avoid deadlock in C programming. Therefore, we have employed the pthread_mutex_lock and pthread_mutex_unlock functions to perform that. Using these functions in our code snippets, we have ensured the usage of resources for one thread at a time.