C++ Multithreading: Understanding Threads

Share the Article

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

Basic example of c++ threads

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

Passing arguments with C++ thread

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

C++ threads with multiple threads in  execution

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

Using  threads with detach

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

Using threads with join function call

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

threads join function throws an exception

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

Using threads with joinable check

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-

  1. job of 1st thread is to push item 10 in to stack for 5 times.
  2. job of 2nd thread is to push item 20 in to stack for 5 times.
  3. 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

Using threads with mutex

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

thread example with condition variable

Main Funda: C++ Multithreading is a very powerful feature but needs proper synchronization mechanism.

Related Topics:

Lambda in C++11
Lambda in C++17
What are the drawbacks of using enum ?
Which member functions are generated by compiler in class?
How to stop compiler from generating special member functions?
Compiler Generated Destructor is always non-virtual
How to make a class object un-copyable?
Why virtual functions should not be called in constructor & destructor ?
Explaining C++ casts
How pointer to class members are different ?
How std::forward( ) works?
Rule of Three
How std::move() function works?
What is reference collapsing?
How delete keyword can be used to filter polymorphism
emplace_back vs push_back

Share the Article

Leave a Reply

Your email address will not be published. Required fields are marked *