Mutual Exclusion – The Curse of Threads

Ths is a direct sequel to my previous post about Threads.

Race Condition / Data Inconsistency ?

Threads. All seems well until you start exchanging data between threads. Data is mainly shared between threads through global variables. When 2 or more threads in the same  program access the same variable without being careful it leads to utter chaos, and there is no way to predict the state of variables . The following example illustrates

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


int num = 0;


void* square(void* data)
{

	while(1)
	{
		printf("The Number is %d\n",num);
		sleep(2);
		printf("And its square is %d\n",num*num);
		sleep(1);
	}
}

void* inc(void* data)
{
	while(1)
	{
		num++;
		sleep(1);
	}
}

void main()
{
	pthread_t t1,t2;
	pthread_create(&t1,NULL,square,NULL);
	pthread_create(&t2,NULL,inc,NULL);
	pthread_join(t1,NULL);
}

The output

The Number is 0
And its square is 4
The Number is 3
And its square is 25
The Number is 7
And its square is 64
The Number is 9
And its square is 121
The Number is 12
And its square is 196
The Number is 15
And its square is 289
The Number is 18
And its square is 400
The Number is 21
And its square is 529
The Number is 24
And its square is 676

Di section

So what’s happening here. t1 is given the job of displaying num, waiting for 1 second and then displaying the square. But by that time num is already modified by t2.
This illustrates a potential nightmare for programmers. If t1 was doing some other computation instead of sleep and again tried to access num there is no guarantee that it would remain the same.

Enter – The Mutex

Mutexes can be though of as a room that can be locked from the inside. If a room has been occupied and locked from the inside, another person trying to enter and lock the room has to wait.

The following program illustrates.

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


int num = 0;
pthread_mutex_t lock;

void* square(void* data)
{

	while(1)
	{
		pthread_mutex_lock(&lock);
		printf("The Number is %d\n",num);
		sleep(2);
		printf("And its square is %d\n",num*num);
		pthread_mutex_unlock(&lock);
		sleep(1);

	}
}

void* inc(void* data)
{
	while(1)
	{
		pthread_mutex_lock(&lock);
		num++;
		pthread_mutex_unlock(&lock);
		sleep(1);

	}
}

void main()
{
	pthread_t t1,t2;
	pthread_mutex_init(&lock, NULL);
	pthread_create(&t1,NULL,square,NULL);
	pthread_create(&t2,NULL,inc,NULL);
	pthread_join(t1,NULL);
}

The Output

The Number is 0
And its square is 0
The Number is 1
And its square is 1
The Number is 2
And its square is 4
The Number is 3
And its square is 9
The Number is 4
And its square is 16
The Number is 5
And its square is 25
The Number is 6
And its square is 36
The Number is 7
And its square is 49
The Number is 8
And its square is 64
The Number is 9
And its square is 81
The Number is 10
And its square is 100

Explanation

pthread_mutex_t lock;

Each mutex is a variable of type pthread_mutext_t.An important thing to remember is that mutexes must be declared in the scope of the variable you wish to protect. Since we are concerned with num which is global, our mutex lock is also global.

pthread_mutex_init(&lock, NULL);

All mutexes must be initialized with pthread_mutex_init. The first argument is a pointer to the mutex, The second argument is a pointer of type pthread_mutexattr_t.It specifies the type of the mutex. Since we have specified NULL the default type is used.

pthread_mutex_lock(&lock);

Acquires the lock .( Locks the room )

pthread_mutex_unlock(&lock);

Releases the lock . ( Unlocks the room and leaves )

Here’s how it works

Before t1 tries to print the value of num it acquires the lock by calling pthread_mutex_lock. At this point even if t2 want to increment num it is made to wait when it calls pthread_mutex_lock. Only after t1 has called pthread_mutex_unlock t2 gets access to num and increments it.

Same goes for t2, When t2 is incrementing after calling pthread_mutex_lock t2 is made to wait when it calls pthread_mutex_lock. t1 only proceeds when t2 calls pthread_mutex_unlock.

In this way only one of t1 and t2 get to use num.

Data Inconsistency is avoided. Problem Solved !

Multi-threading poses yet one final major problem, Deadlocks, more about it here

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s