Mutex in C++ (Critical Section Problem Part-2)

So critical section is a block of code, where threads/processes access the shared resources/variables. So to avoid inconsistencies, at a time only one thread must be able to access the critical section. 

If one thread is executing in the critical section, another thread should wait until thread one completes its execution. 

The process where at a time only one thread executes in the critical section is called mutual exclusion. It is one of the conditions to avoid the deadlock. We will learn about deadlock at the later stage of this course. 

So let’s recap the previous tutorial. At lines number 9 and 10, both the threads t1 and t2 were accessing the shared variable x simultaneously. It is the reason we get different values as output. So we have to block thread t1 if t2 is accessing this code and vice versa. 

One of the solutions to handle this situation is a mutex. 

Mutex is a mutual exclusion object to synchronizes access to the resources. A mutex has two states, 

  1. acquire(thread acquires the mutex) 
  2. release(thread release the mutex)

Mutex acquired by one thread/process can only be released by the same thread/process.

Let’s understand it with the previous example:

#include<iostream>
#include<thread>
using namespace std;

int x = 0;
std::mutex m;

void function1()
{
	//acquire the lock
	m.lock();

	//critical section code
	x = 2;
	x = x+1;

	//release the lock
	m.unlock();

}
int main()
{
	std::thread t1(function1);
	std::thread t2(function1);
	
	if(t1.joinable())
	{
		t1.join();
	}

	if(t2.joinable())
	{
		t2.join();
	}

	cout<<"Values of x:\t"<<x<<endl;

	return 0;
}

You can run the code using below command:

g++ -std=c++14 -pthread multithreadingProblem.cpp
./a.out

Output of the code:

Values of x:	3

Let’s understand, how the code is working:

At line number 6, we are defining the mutex variable m. By default the mutex variable is unlock, means any thread can acquire it initially. So let suppose thread t1 calls the function1, and acquire the mutex m at line number 11. At the same time, context switch takes place and thread t2 starts executing, and reaches at line number 11. When it tries to acquire the mutex, it comes to know that it has already been acquired by thread t1. So it wait until thread t1 releases the lock at line number 18. As soon as mutex is released by thread t1, t2 starts executing in the critical section(Just reverse will take place when t2 calls the function1 first). Thus we ensure that at a time only single thread executes in the critical section and avoid the ambiguous output.

Properties of Mutex:

  1. At a time, only one thread can acquire the mutex.
  2. mutex will be released by same thread which has acquired it.
  3. It is a mechanism to ensure that at a time only one thread executes in the critical section.
  4. lock and unlock are the atomic operations i.e. no context switch will take place during either of the operations.
Posted in C++