Sunday, August 5, 2012

Posix Thread synchronization with Mutex

Mutex is a synchronization technique to protect shared resources. It can be thought of a lock, which is used to protect a resource. Similarly when one thread lock a region or piece of code (normally called critical section), then no other thread can access or execute the locked code.

Here we will use POSIX mutex API to see the use of mutex lock. Below are some important POSIX functions, that we will use in our example.

1. Init Mutex

int pthread_mutex_init(pthread_mutex_t *mutex, const pthrea. d_mutexattr_t *attr);

This function initializes a mutex pointed to by mutex. The pthread_mutexattr_t param is for specifying attribute for the mutex.

2. Lock,Unlock and Destroy

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);

The above functions are self-explanatory.

3. Points to remember while using mutex.

1. No thread should attempt to lock or unlock a mutex that has not been initialized.
2. The thread that locks a mutex must be the thread that unlocks it.
3. No thread should have the mutex locked when you destroy the mutex.
4. Never call pthread_mutex_lock on a mutex that it has already locked.

4. Code Example
We wiil create small and simple program to understand Mutex. We'll call it mutex.c
mutex.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>

pthread_mutex_t ml;
int counter = 0;

void* start_function(void* value)
{
printf("%s is now entering the thread function.\n", (char*)value);
pthread_mutex_lock(&ml);
counter++;
sleep(2);
pthread_mutex_unlock(&ml);
printf("%s is now leaving the thread function.\n", (char*)value);
printf("Value of counter is: %d\n", counter);
pthread_exit(value);
}

main()
{
int res;
pthread_t thread1, thread2;

res = pthread_mutex_init(&ml, NULL);
if (res != 0) {
perror("Mutex Init failed.");
exit(EXIT_FAILURE);
}

res = pthread_create(&thread1, NULL, start_function, "Thread1");
if (res != 0) {
perror("Creation of thread failed");
exit(EXIT_FAILURE);
}

res = pthread_create(&thread2, NULL, start_function, "Thread2");
if (res != 0) {
perror("Creation of thread failed");
exit(EXIT_FAILURE);
}

res = pthread_join(thread1, NULL);
if (res != 0) {
perror("Joining of thread failed");
exit(EXIT_FAILURE);
}

res = pthread_join(thread2, NULL);
if (res != 0) {
perror("Joining of thread failed");
exit(EXIT_FAILURE);
}

pthread_mutex_destroy(&ml);
}

Now when you compile and run the program, you will see:

gcc -o mutex mutex.c -lpthread
./mutex
Thread1 is now entering the thread function.
Thread2 is now entering the thread function.
Thread1 is now leaving the thread function.
Value of counter is: 1
Thread2 is now leaving the thread function.
Value of counter is: 2

The above progarm has a critical section of code, which increases the counter. And this piece of code can be executed by multiple threads simulataneously in a multi-threaded programming. So a mutex lock is used to protect it.

What will happen if we don't use mutex? Then the output will be:

./mutex
Thread1 is now entering the thread function.
Thread2 is now entering the thread function.
Thread2 is now leaving the thread function.
Value of counter is: 2
Thread1 is now leaving the thread function.
Value of counter is: 2

This is not the output, which you have expected. So in a multi-threaded program if you don't protect critcal section, then the result is not guaranteed.

No comments:

Post a Comment