- Introduction
- Evaluating Locks
- Mutex APIs
- Advantages of Mutex
- Disadvantages of Mutex
- Relavant Posts
- Sample Program
Introduction:
- It is a mutual exclusion object that is used to prevent data inconsistencies due to race conditions.
- A race condition often occurs when two or more threads need to perform operations on the same memory area, but the results of computations depending on the order in which these operations are performed.
- One can apply a mutex to protect a segment of memory (“critical region”) from other threads that are synchronized access to the shared resources.
- It is a locking mechanism where a thread needs to acquire a lock before entering its critical section and unlock once finishes.
- Mutex object lock is released only by the threads that have acquired the lock.
- The other processes or threads must wait that is its thread descriptor(TD) is added to the wait queue and is woken when the lock is released.
- Mutexes can be applied only to threads in a single process and do not work between processes as do semaphores.
Evaluating Locks
Any locking mechanism must be evaluated on below three factors:
- Solution to Critical Section : Does it ensures that only one thread or process enters the CS.
- Fairness : Does it ensure that each process or threads get a chance to enter the CS and not starved.
- Performance : This considers the time overhead added by using lock.
Mutual exclusion makes sure that only one thread may execute the critical section of code at a time. Any other thread that tries to do so will go to sleep until the lock is released.

Mutex related API:
POSIX has provided some set of API which can be used for mutual exclusion. Those are:
- pthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutex_attr_t *attr);
- pthread_mutex_lock (pthread_mutex_t *mutex);
- pthread_mutex_unlock (pthread_mutex_t *mutex);
- pthread_mutex_destroy (pthread_mutex_t *mutex);
- pthread_mutex_trylock (pthread_mutex_t *mutex);
Initialize a Mutex
int pthread_mutex_init (pthread_mutex_t *mutex, const pthread_mutex_attr_t *attr);
Lock a Mutex:
int pthread_mutex_lock(pthread_mutex_t *mutex):
- This API is used to lock the mutex object referenced by mutex.
- If the mutex is already locked, the calling thread is blocked until the mutex becomes available.
- Attempting to relock the mutex which is already locked causes deadlock, as the lock can be released by the thread which acquired the lock and that itself is blocked. Hence this should be considered while using mutex.
- On success it returns 0 and on failure, an error code is returned.
Unlock a Mutex:
int pthread_mutex_unlock (pthread_mutex_t &mutex):
- This API is used for releasing the mutex object referenced by mutex.
- On success it returns 0 and on failure, an error code is returned.
Destroy a Mutex:
int pthread_mutex_destroy (pthread_mutex_t *mutex);
- The pthread_mutex_destroy() function shall destroy the mutex object referenced by mutex; the mutex object becomes, in effect, uninitialized.
- On success it returns 0 and on failure, an error code is returned.
Trylock a Mutex:
int pthread_mutex_trylock (pthread_mutex_t *mutex);
- This API is similar to pthread_mutex_lock, only difference lies in the fact that is unblocking call that even if mutex is locked initially, it does not causes the calling thread to block, it returns immediately.
- However, it probably makes more sense to use pthread_mutex_lock() instead of pthread_mutex_trylock(), so that y thread will wait for the mutex to be available if it is contended.
Advantages of Mutex:
- There are no race conditions and data always remain consistent due to the fact that, in mutex, only one thread is in critical section at any given time.
- The thread with mutex has ownership over the resource.
- Mutex is typically atomic and singular in nature.
Disadvantages of Mutex:
- It is difficult to lock or unlock mutex from a different context than the one that acquired it.
- In case of busy waiting state the CPU time is wasted.
- If a thread obtains a lock and in the process it is preempted, then, the other thread may not be able to move
Sample program(Without proper Synchronization(mutex):
#include <stdio.h> #include <string.h> #include <pthread.h> #include <stdlib.h> int counter = 0; void *fun(void *arg) { counter++; printf("\n Job %d is started\n", counter); sleep(2); printf("\n Job %d is finished\n", counter); counter++; return NULL; } int main() { int cnt =0; pthread_t th_id[2]; while(cnt <2) { int res = pthread_create(&th_id[cnt], NULL, fun, NULL); if(res) { printf("\n Thread %d creation failed %s\n", cnt, strerror(res)); exit(0); } cnt++; } pthread_join(th_id[0], NULL); pthread_join(th_id[1], NULL ); }
Output:
[aprakash@wtl-lview-6 thread]$ gcc mutex.c -lpthread [aprakash@wtl-lview-6 thread]$ ./a.out Job 1 is started Job 2 is started Job 2 is finished Job 2 is finished
The following conclusion can be drawn from above output:
- The log “job 2” started just after log “job 1” started, this clearly states that before the “job 1” is over, the thread 1 is preempted and scheduler picks the thread 2 that is starts the “job 2”.
- If the above assumption is correct, means the value of variable counter is incremented before the job 1 finished.
- So when the “job 1” actually got finished , the value of counter(shared variable) is 2 or may be first “job 2” got finished and then “job 1”, depends upon scheduler.
- And then the “job 2” got finished, hence there is a repetitive log.
Now this problem can be solved with the help of proper synchronization that is using a mutex.
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <string.h> #include <pthread.h #include <errno.h> /* Having a lock variable */ pthread_mutex_t lock; int counter = 0; void *fun (void *arg) { int res = pthread_mutex_lock(&lock); if(res) { printf("\n Mutex lock failed %s\n", strerror(res)); exit(0); } counter++; printf("\n Job %d is started\n", counter); sleep(2); printf("\n Job %d is finished\n", counter); pthread_mutex_unlock(&lock); return NULL; } int main() { int cnt =0; pthread_t th_id[2]; pthread_mutex_init(&lock, NULL); while(cnt <2) { int res = pthread_create(&th_id[cnt], NULL, fun, NULL); if(res) { printf("\n Thread %d creation failed %s\n", cnt, strerror(res)); exit(0); } cnt++; } pthread_join(th_id[0], NULL); pthread_join(th_id[1], NULL ); pthread_mutex_destroy(&lock); return 0; }
Output:
[aprakash@wtl-lview-6 thread]$ gcc solution_cs_mutex_use.c -lpthread [aprakash@wtl-lview-6 thread]$ ./a.out Job 1 is started Job 1 is finished Job 2 is started Job 2 is finished
Sample Program (To show deadlock, if attempt to relock):
#include <stdio.h> #include <string.h> #include <pthread.h> #include <stdlib.h> #include <errno.h> #include <pthread.h /* Having a lock variable */ pthread_mutex_t lock; int counter = 0; void *fun(void *arg) { int res = pthread_mutex_lock(&lock); if(res) { printf("\n Mutex lock failed %s\n", strerror(res)); exit(EXIT_FAILURE); } /* Tried to relock again, hence deadlock */ pthread_mutex_lock(&lock); counter++; printf("\n Job %d is started\n", counter); sleep(2); printf("\n Job %d is finished\n", counter); pthread_mutex_unlock(&lock); return NULL; } int main() { int cnt =0; pthread_t th_id[2]; pthread_mutex_init(&lock, NULL); while(cnt <2) { int res = pthread_create(&th_id[cnt], NULL, fun, NULL); if(res) { printf("\n Thread %d creation failed %s\n", cnt, strerror(res)); exit(0); } cnt++; } pthread_join(th_id[0], NULL); pthread_join(th_id[1], NULL ); pthread_mutex_destroy(&lock); return 0; }
Output:
[aprakash@wtl-lview-6 thread]$ gcc solution_cs_mutex_relock.c -lpthread [aprakash@wtl-lview-6 thread]$ ./a.out
Sample Program( unblocking trylock):
#include <stdio.h> #include <string.h> #include <pthread.h> #include <stdlib.h> /* Having a lock variable */ pthread_mutex_t lock; int counter = 0; void *fun(void *arg) { pthread_mutex_lock(&lock); /* Tried to relock again using trylock, hence no deadlock*/ pthread_mutex_trylock(&lock); counter++; printf("\n Job %d is started\n", counter); sleep(2); printf("\n Job %d is finished\n", counter); pthread_mutex_unlock(&lock); return NULL; } int main() { int cnt =0; pthread_t th_id[2]; pthread_mutex_init(&lock, NULL); while(cnt <2) { int res = pthread_create(&th_id[cnt], NULL, fun, NULL); if(res) { printf("\n Thread %d creation failed %s\n", cnt, strerror(res)); exit(0); } cnt++; } pthread_join(th_id[0], NULL); pthread_join(th_id[1], NULL ); pthread_mutex_destroy(&lock); return 0; }
Relevant Posts:
- Thread Synchronization
- Critical Section
- Race Condition
- Semaphore
- Mutex Vs Semaphore
- Spinlock
- Spinlock Vs Mutex
- Condition Variables
.
Categories: Operating system (OS)
Leave a Reply