C++ Multithreading is a powerful feature. This article explains the entire concept behind this concept.
Why Threads ?
Threads are mainly required for doing multiple jobs at one time without waiting for one job to complete.
For example, we have an application which need to perform multiple jobs as below –
- Taking input from keyboard.
- Formatting of data
- Displaying Data on Screen.
Single Threaded and Multi Threaded Applications
Single Threaded Application will perform all above jobs in serial manner . Firstly, it will take input from user .Secondly, it will format data .Thirdly , it will Display data. One job need to wait for other jobs .
But in case of Multiple threaded application we can dedicate one thread for each job. Therefore, all jobs shall execute in parallel. For each job, we shall create following threads in C++ program.
- Input thread -whose job is to take input from Keyboard and give to formatter thread.
- Formatter thread – take Data from input thread and format it and give to Display thread.
- Display thread – on receiving data display it to the screen.
So with C++ Multithreaded approach for above application we have increased the overall efficiency of the above application by introducing threads for doing jobs in efficient manner .In above example we have given each thread a dedicated responsibility so that overall job can be completed fast.
Threads included in below Header file
#include <thread>
How to compile the code which uses C++ Multithreading
For compiling main.cpp for C++ Multithread need to include -pthread options with g++
g++ -std=c++11 -pthread main.cpp -o main.out
Basic example with just single thread
Below example is demonstrating an application in which we have created a single thread.
First Step:
Firstly, we need to create an object job of std::thread class. To this (job) thread object, a function performtask( ) is attached. This function embeds the responsibility of job thread. This statement shall automatically start execution of job thread in parallel to main program thread.
Second Step:
To ensure that the main program thread shall wait for job thread to finish its task, the program must call the thread function job.join( ). If we do not call job.join() main thread shall exit without waiting for job thread.
#include<thread> //for threads
#include<iostream> //main header
using namespace std;//for namespace
void performTask()
{
cout<<"performTask"<<endl;
}
int main()
{
std::thread job( performTask);
job.join();
cout<<"Main job done"<<endl;
}
The output of above application will be
Passing Function arguments in thread
Below example is demonstrating same basic application where we have created a single thread but now also pass parameter from main thread.
#include<thread> //for threads
#include<iostream>//main header
using namespace std;//for namespace
void performtask(int jobInt,char jobChar)
{
cout<<"parameter int "<<jobInt<< endl;
cout<<" parameter char " <<jobChar<<endl;
}
int main()
{
int jobInput =3;
char jobChar ='c';
std::thread job(performtask,jobInput,jobChar);
job.join();
cout<<" Main job done"<<endl;
}
The output of above application will be
C++ Multithreading example: Starting multiple threads together
Below example is demonstrating an application where we have created multiple threads and also passed parameters from main thread. In below application we can see that multiple threads are active and performing their jobs together . No thread is waiting for other to perform job.
#include<thread> //for threads
#include<iostream> //main header
using namespace std;//for namespace
void performTask(int value)
{
for(int i =0;i<5;i++)
{
cout<<"performTask"
<<value << endl;
}
}
int main()
{
std::thread job1(performTask,1);
std::thread job2(performTask,2);
std::thread job3(performTask,3);
job1.join();
job2.join();
job3.join();
cout<<"Main job done"<<endl;
}
Output
Detaching a thread from the main thread (program)
Thread detach feature mainly detaches a main thread object from thread in execution .The moment we called detach on thread object, this thread execution becomes independent.
For example , if we want to create a background thread which should keep running even when the main program exits. In such case, we shall call detach on thread object.
The following example demonstrates such application where we have created a thread object showdata and then we have called detach on this thread.
#include<thread> //for thread
#include<iostream>//main header
#include<chrono> //for date time
using namespace std;
void processData()
{
while(1)
{
cout<<"Hi MainFunda!"<<endl;
std::this_thread::sleep_for (std::chrono::seconds(1));
}
}
int main()
{
std::thread showData(processData);
showData.detach();
cout<<"Main exited" << endl;
}
Output
C++ Multithreading: Joinable Threads
Thread join is mainly used if one thread wants to wait for other thread. For example, thread t1 want to wait for thread t2 to exit before doing some task t1 need to call t2.join( ) .
In below example main thread want to wait for t1 thread to finish its job. Therefore, code in main thread shall call t1.join( ).
#include<thread> //for threads
#include<iostream>//main header
#include<chrono> //for date time
using namespace std; //for namespace
void show()
{
int count = 0;
while(count++<5)
{
cout<<"Main Funda"<<endl;
std::this_thread::sleep_for (std::chrono::seconds(1));
}
}
int main()
{
std::thread t1(show);
t1.join();
cout<<"Main job Done"<<endl;
}
The Output of below programme will be
What if the thread already exited before join
It may happen that t1 has already exited or t1 doesn’t have any thread associated with it in this case program shall terminate .
#include<thread> //for threads
#include<iostream> //main header
#include<chrono> //for date time
using namespace std; //for namespace
void show()
{
cout<<"Main Funda"<<endl;
std::this_thread::sleep_for (std::chrono::seconds(1));
}
int main()
{
std::thread t1(show);
t1.join();
t1.join(); //program will crash
cout<<"main exited"<<endl;
}
Output
Remedy – joinable( ) check
In order to save program from crash we can check the thread using joinable function.
#include<thread> //for threads
#include<iostream> //main header
#include<chrono> //for date time
using namespace std; //for namespace
void show()
{
cout<<"Main Funda"<<endl;
std::this_thread::sleep_for (std::chrono::seconds(1));
}
int main()
{
std::thread t1(show);
t1.join();
if(t1.joinable())
{
t1.join(); //program will not crash
}
cout<<"main exited"<<endl;
}
Output
Threads Synchronization – Race Conditions
Thread synchronization is mainly used for avoiding race conditions between threads. Race condition mainly occurred if 2 or more threads want to operate on same data or resources.
For example, We have one thread for updating global data and other thread for reading global data. Due to multiple access of global data at same time with multiple threads can corrupt the data. Therefore, we need to synchronize threads while accessing same data or resource so let one thread finish updating then only other thread read data. In order to synchronize data in C++ we have several mechanism such as
- Mutex,
- Condition Variables
- Semaphores etc.
Below we have discussed some mechanism.
Thread – Example for Mutex
For instance , we have data or shared resource that we want to access from multiple threads. For data protection mutex is ideal option for us to synchronize threads . Thread need to lock and unlock mutex during access of shared data or resource.
Header File
#include<mutex>
Assume the Scenario
We can take an application which is having a stack object as shared resource. This resource needs access from 2 threads doing below jobs-
- job of 1st thread is to push item 10 in to stack for 5 times.
- job of 2nd thread is to push item 20 in to stack for 5 times.
- After both threads are done, then main thread needs to print content of stack.
We need to guard the stack so that threads should not corrupt stack
(one thread can override other so that total element is less than 10).
#include <iostream> //main header
#include <thread> //for thread
#include <mutex> //for mutex
#include<stack> //for stack
using namespace std; //for namespace
std::mutex stacklock; //MUTEX
std::stack<int> s1; //Common Resource
void pushData (int n)
{
stacklock.lock(); //MUTEX lock
for (int i=0; i<5; ++i)
{
s1.push(n);
}
stacklock.unlock(); //MUTEX unlock
}
int main ()
{
std::thread stackpush1 (pushData,10);
std::thread stackpush2 (pushData,20);
stackpush1.join();
stackpush2.join();
while(!s1.empty())
{
cout<<s1.top()<< " ";
s1.pop();
}
cout << endl;
return 0;
}
The output of above application will be all 10 and 20’s together .No corruption
Threads – Example for Condition Variable
For example , we have condition where one thread need to wait for other thread for doing its job .In this case we need to use condition variable for synchronizing threads . A condition variable always linked to mutex.
Let us take an example where we have 2 threads one thread in incrementing value of shared variable by 5 and other thread need to display updated value .So displaythread need to wait for incrementing thread. In order to solve this problem we can take help for condition variable as demonstrated below
#include <iostream> //main header
#include <thread> //for thread
#include <mutex> //for mutex
#include <condition_variable> //for condition variable
#include<chrono> //for date time
using namespace std; //for namespace
std::mutex mvalue;
std::condition_variable condvariable;
int value = 0;
void incrementvalue() {
std::this_thread::sleep_for (std::chrono::seconds(1));
std::lock_guard<std::mutex> lock(mvalue);
value = value +5;
cout<<"increment Thread Notify Display thread"<<endl;
condvariable.notify_one();
}
void displayValue () {
cout<<"Display thread waiting"<<endl;
std::unique_lock<std::mutex> lock(mvalue);
cout<<"Display thread waiting mutex"<<endl;
condvariable.wait(lock);
std::cout << "Value after Incrementing" << value << endl;
}
int main ()
{
std::thread displayThread(displayValue);
std::thread incrementThread(incrementvalue);
incrementThread.join();
displayThread.join();
cout<< "Main job done"<<endl;
return 0;
}
The output of above application will be