A Brief Introduction To Multithreading In C++.
In this tutorial, we will get an overview of multithreading in C++.
So what is a thread? A thread is a working unit of a particular process. In multi-programming operating systems, different processes are executing simultaneously.
In a similar way, we may want to execute the same process instances simultaneously. Each process instance, in this case, is assigned to an execution unit called thread. In a multithreading system, numerous threads execute simultaneously independent of one another.
=> Take A Look At The C++ Beginners Guide Here.
Prior to C++ 11, we had POSIX thread support. But this feature had serious portability issues as it worked only on the Linux or UNIX operating system. Thus from C++ 11 onwards, we have a single class std:: thread which defines all the functionality for threads. The classes and functions are defined in the header file.
Table of Contents:
Working Of <thread>
Using std:: thread we simply need to create a new thread object and pass it a callable. A callable is an executable code that we want to execute when the thread is running. So whenever we want a new thread, we just create an object of std:: thread and pass a callable as an argument to its constructor.
Once the std:: thread object is created, a new thread is launched and the code provided by callable is executed.
Let us see how we can define a callable to be provided to the thread object.
A callable can be defined in three ways.
#1) Using The Function Object
We can use a function object as a callable in the thread object. For using the function object, we need to have a class and in that class, we overload the operator (). This overloaded function contains the code to be executed when the thread is created.
/</em>/ Define the class for function object class functioObject_class { // Overload () operator void operator()(params) { // code to be executed } }; // Create thread object
std::thread thread_object(functioObject_class (), params)
Note the way in which the thread object is defined. As the first parameter to the constructor of thread object, we provide the overloaded function and then specify its arguments (params) as the second argument.
#2) Using Function Pointer
A callable using function pointer can be defined in the following way.
void funct_call(params) //code to be executed }
Once we define this function, we can create a thread object with this function as callable, in the following way.
std::thread thread_obj(funct_call, params);
Note that the arguments (params) passed to the function, are provided after the function name in the thread object.
#3) Using A Lambda Expression
We can also have a callable as a lambda expression and pass it to the thread object for execution. The code snippet for the same is shown below.
// Define a lambda expression auto f = [](params) { // code for execution };
std::thread thread_object(f, params);
In the above code, we have defined a lambda expression f and we then pass it to the thread object constructor as the first argument followed by its parameters (params) as the second argument.
std::thread join method
In some cases, we might want the currently executing thread to finish before we start another action.
A classic example is when we open the GUI application. The moment we open the application, a thread to load and initialize the GUI is started and we cannot perform any action unless loading and initializing is done correctly so as to ensure that the GUI functions properly.
The class std:: thread provides a join() method which ensures that the current thread (pointed by *this) finishes first before any other action is taken.
Take the following example,
int main() { std::thread t1(callable_code); ….. t1.join(); ….. }
In the above example, the main function will have to wait to continue until thread t1 finishes. In general, the join function of thread blocks other actions/functionality until the thread calling finishes its execution.
Example Of Thread
We present a complete coding example for creation and execution of the thread in the program shown below.
#include <iostream> #include <thread> using namespace std; // function to be used in callable void func_dummy(int N) { for (int i = 0; i < N; i++) { cout << "Thread 1 :: callable => function pointer\n"; } } // A callable object class thread_obj { public: void operator()(int n) { for (int i = 0; i < n; i++) cout << "Thread 2 :: callable => function object\n"; } }; int main() { // Define a Lambda Expression auto f = [](int n) { for (int i = 0; i < n; i++) cout << "Thread 3 :: callable => lambda expression\n"; }; //launch thread using function pointer as callable thread th1(func_dummy, 2); // launch thread using function object as callable thread th2(thread_obj(), 2); //launch thread using lambda expression as callable thread th3(f, 2); // Wait for thread t1 to finish th1.join(); // Wait for thread t2 to finish th2.join(); // Wait for thread t3 to finish th3.join(); return 0; }
Output:
Thread 1 :: callable => function pointer
Thread 1 :: callable => function pointer
Thread 3 :: callable => lambda expression
Thread 3 :: callable => lambda expression
Thread 2 :: callable => function object
Thread 2 :: callable => function object
In the above example, we have created three threads using three different callable i.e. function pointer, object, and lambda expression. We create 2 instances of each thread and start them. As shown in the output, three threads operate simultaneously independent of each other.
Recommended reading =>> Thread Testing Guide
Conclusion
In this tutorial, we have seen the multithreading concepts in C++ with a clear example. In our subsequent tutorials, we will learn more C++ topics that would help us write robust and efficient programs.
=> Read Through The Easy C++ Training Series.