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++

Problem with Multithreading (Critical Section Problem Part-1):

Goal: In this article, we will see what are the problems associated with multithreading.

We will take a function with name functionWithCriticalSection, and two threads t1, t2 will call this function.

Let’s see the code:

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

int x = 0;

void functionWithCriticalSection()
{
//this is the critical section
	x = 2;
	x = x+1;
}

int main()
{

	std::thread t1(functionWithCriticalSection);
	std::thread t2(functionWithCriticalSection);
	
	if(t1.joinable())
	{
		t1.join();
	}

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

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

	return 0;

}

To Run the Code:

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

Output of the Code:

Values of x:	3
Values of x:	4

When we run the code multiple times, we see this strange output i.e. in one run we get x = 3, and in the another run we get 4. So we can say for the different runs, we are getting different outputs.

Note: When we you will the above code many times(can be upto 1000 times), then you will get x = 4.

So think about why the code is showing this strange behaviour?

This problem is called critical section problem.

So critical section is a block of code, where two/more threads/processes access the shared variables, and it ends up creating inconsistencies in the code output.


In our code, variable x is the shared variable, and line number 10, 11 is the critical section because this is the part of code where threads are accessing shared variable.

In the next post, we will see how we can deal with the ambiguous output.

Posted in C++

joinable() in c++ Multithreading

We perform thread.join() operation to join the child thread to the main thread. But think about a scenario, you have a large project, and you don’t know whether you have already joined the child thread to the main thread or not. So before joining the child thread to the main thread, we check if thread has already been joined or not.

We can understand it with below example:

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

void function1()
{
	cout<<"This is function1 called by thread1"<<endl;
}

void function2()
{
	cout<<"This is function2 called by thread2"<<endl;
}

int main()
{
	std::thread t1(function1);
	std::thread t2(function2);

	t1.join();

	if(t1.joinable())
	{
		cout<<"Joining the Thread t1"<<endl;
		t1.join();
	}

	if(t2.joinable())
	{
		cout<<"Joining the Thread t2"<<endl;
		t2.join();
	}

	return 0;

}

To run the code:

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

Output of the Code:

This is function2 called by thread2
This is function1 called by thread1
Joining the Thread t2

Lets understand the output of the code:

At lines number 17 and 18, we call function1 and function2. At line number 20, we join thread t1 to the main thread. When we again check if t1 is joinable or not using t1.joinable(), it returns false, thus we do not see output from the first if block. But when we check if t2.joinable() is true or not, it returns true because till this point we have not joined t2 to the main thread. Thus we enter the if block and can see the output from the second if block.

When std::thread::joinable() returns false:

  • If the thread has already joined to the main thread or detached.
  • If the thread has been moved elsewhere.

Posted in C++

Latency in Multithreading Execution vs Sequential Execution:

Goal: In this post, we will understand the difference in execution time in case of multithreading execution vs sequential execution.

To understand which execution takes more time, we will take one simple assumption i.e. the program that we are going to execute does not have any IO operations.

We are going to write two functions:

  1. Function1 (it will calculate the sum of all the odd numbers from 1 to 100).
  2. Function2 (it will calculate the sum of all the even numbers from 1 to 100).

So following is the code for the above operations:

#include<iostream>
#include <pthread.h>
#include<thread>
using namespace std;
using namespace std::chrono;

int oddSum;
int evenSum;

void function1(int val)
{
	oddSum = 0;

	while(val>=0)  //iterate through all numbers from 100 to 0
	{
		if((val&1)==0) //check if number is divisible by 2 or not
		{
			oddSum = oddSum+val;
		}
		val--;
	}
}

void function2(int val)
{
	evenSum = 0;

	while(val>=0) //iterate through all numbers from 100 to 0
	{
		if((val&1)==1) //check if number is divisible by 2 or not
		{
			evenSum = evenSum+val;
		}
		val--;
	}
}

int main()
{
	//calling the function using multithreading
	cout<<"\n***********************************************************"<<endl;
	auto startTime1 = std::chrono::system_clock::now();
	std::thread t1(function1,100);
	std::thread t2(function2,100);

	t1.join();
	t2.join();
	
	cout<<"Sum of all even numbers from 1 to 100:\t"<<evenSum<<endl;
	cout<<"Sum of all odd numbers from 1 to 100:\t"<<oddSum<<endl;
	auto endTime1 = std::chrono::system_clock::now();

	auto duration1 = duration_cast<microseconds>(endTime1-startTime1);
	cout<<"Time taken using Multithreaded Execution:\t"<<duration1.count()<<endl;

	//calling the function using sequential execution

	cout<<"\n***********************************************************\n"<<endl;

	auto startTime2 = std::chrono::system_clock::now();
	function1(100);
	function2(100);

	cout<<"Sum of all even numbers from 1 to 100:\t"<<evenSum<<endl;
	cout<<"Sum of all odd numbers from 1 to 100:\t"<<oddSum<<endl;
	auto endTime2 = std::chrono::system_clock::now();

	auto duration2 = std::chrono::duration_cast<microseconds>(endTime2-startTime2);
	cout<<"Time taken using Sequential Execution:\t"<<duration2.count()<<endl; 

	return 0;
}

Let’s understand the above code:

At line number 42, we are recording the start time of the multithreaded execution. We call function1 and function2 at Line numbers 43 and 44 respectively. Once the execution of thread t1 and t2 completes, we join both the threads to the main thread and print the result. After the completion of all the steps, we again record the time and print it in microseconds.

Now, we perform the same operations again, but this time in the sequential fashion and, print the execution time and result.

Now, think about the scenario, the code is not having any I/O operations, which one (Multithreaded or Sequential Execution) will have more execution time. You can run the above code on your system using the below command:

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

Below are the results when we execute the code on our system(results might be different on different machines due to the hardware change):

***********************************************************
Sum of all even numbers from 1 to 100:	2500
Sum of all odd numbers from 1 to 100:	2550
Time taken using Multithreaded Execution:	255

***********************************************************

Sum of all even numbers from 1 to 100:	2500
Sum of all odd numbers from 1 to 100:	2550
Time taken using Sequential Execution:	6

When you will look at the results, you will wonder that multithreaded execution is taking more time as compared to sequential execution. Why is that the case?

Let’s understand it.

So when we are calling function1 and function2 using thread t1 and t2. Many context switches take place between thread t1 and t2 i.e. for x microseconds t1 runs and y microseconds t2 runs and so on. But when a context switch takes place, there is some latency associated with the context switch. It results in increasing the execution time for multithreaded execution.

So let suppose k numbers of context switches take place during the multithreaded execution then total execution time can be calculated using the below formula:

Execution time using Multithreading = function1 exe. time + function2 execution time + Number of context Switches * Latency in One Context Switch

Time Taken In Multithreaded Execution

But in case of sequential execution, function1 executes first and then function2. There is no context switch associated with the sequential execution. So time taken in sequential execution can be calculated using the below formula:

Execution time using Sequential Execution= function1 execution time + function2 execution time

Time in Sequential Execution

This blog might be an interesting read: Multithreading and MultiProcessing using Python

Posted in C++

Call the function with parameters

Goal: In this tutorial, we will see how we can pass the parameters to the function called using thread.

We will create two threads t1 and t2. One thread will call function1 (this function will perform the addition of two numbers), and another thread will call function2 (this function will perform the multiplication of two numbers).

Below is the code for this exercise:

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

int multi;//variable to store the multiplication of two numbers
int sum; //variable to store the sum of two numbers

void function1(int x, int y)
{
	sum = x+y;
}

void function2(int x, int y)
{
	multi = x*y;
}

int main()
{
	int a = 10, b = 20;

	std::thread t1(function1,a,b);
	std::thread t2(function2,a,b);

	t1.join();
	t2.join();

	cout<<"Sum of given numbers: "<<sum<<endl;
	cout<<"Multiplication of given numbers: "<<multi<<endl;

	return 0;
}

std::thread object takes two types of parameter,

  1. Function name 
  2. Values that we want to pass to the called function. 

So std::thread t1(function1,a,b) creates the thread t1 and calls the function1 with parameter a and b.

Once the execution of t1 and t2 completes, both the threads are joined to the main thread using join() function/method and we print the output of function1 and function2 using global variables sum and multi.

Posted in C++

Join in C++ Multithreading

From the line number 20 and 21 in the below code, if we remove t1.join() and t2.join(), our main thread will not wait for t1 and t2 to gets complete. So even before the execution of t1 and t2, main thread will terminate, and program execution will complete, so we will not be able to see the output of thread t1 and thread t2.

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

void function1()
{
	cout<<"This is function1 called by thread1"<<endl;
}

void function2()
{
	cout<<"This is function2 called by thread2"<<endl;
}

int main()
{
	std::thread t1(function1); //creation of thread1 and calling the function1
	std::thread t2(function2); //creation of thread2 and calling the function2

	t1.join(); //waiting for thread1 to execute
	t2.join(); //waiting for thread2 to execute

	return 0;
}
Command to Execute the code:
g++ -std=c++14 -pthread threadCreation.cpp
./a.out

You can try executing the above code by removing line number 20 and 21. You will not see the output from function1 and function2.

So why are we not able to see the output of function1 and function2? Let’s try to understand this.

At line number 18, we are having three threads in our system, one is the main thread, and the other two are t1 and t2. Main thread is the parent thread and t1, t2 are child threads. Child threads will be there in the system till the time parent thread is there in the system. So when t1 and t2 go for the function execution, parent thread i.e. main thread completes its execution and terminates. Now in the system, we are having only two threads t1, t2 and their parent thread i.e. main thread is not there in the system(it has completed its execution). So t1 and t2 also terminate without generating their output.


So if we use t1.join() and t2.join(), it means main thread is waiting for t1 and t2 to complete their execution and we see the output from function1 and function2.

Posted in C++

Thread Creation in C++

Goal:

At the end of this tutorial, you will learn creating threads and how we can call the different functions using them.


So for a better understanding of the topic, we are going to take a simple example. We will create two threads t1, and t2. These two threads will call two functions, function1 and function2. Once the execution of both the threads is complete, they will be joined to the main thread. So below is the code for this scenario:

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

void function1()
{
	cout<<"This is function1 called by thread1"<<endl;
}

void function2()
{
	cout<<"This is function2 called by thread2"<<endl;
}

int main()
{
	std::thread t1(function1); //creation of thread1 and calling the function1
	std::thread t2(function2); //creation of thread2 and calling the function2

	t1.join(); //waiting for thread1 to execute
	t2.join(); //waiting for thread2 to execute

	return 0;
}
Command to Execute the code:
g++ -std=c++14 -pthread threadCreation.cpp
./a.out


In main, at lines number 17 and 18, we are creating the two threads t1 and t2. Thread t1 is calling function1 and thread t2 is calling function2.
Once the execution of threads t1 and t2 are complete, both the threads are joined to the main thread.

When we run this code multiple times, we get different outputs. The reason for this is, both the threads t1 and t2 are created simultaneously. But due to the slight time difference in thread creation/execution, sometimes we see output from thread t2 first and then t1 and vice versa.

Note: Before moving ahead to the next tutorial, think about why are we using t1.join() and t2.join()?

Posted in C++