Imagine a scenario where you are texting your friend and also doing your homework. Here you are not actually doing both the tasks together, but switching between them. This is multitasking and computers do it often, but they are so good at it that we humans never even notice. Multithreading is where you actually perform both tasks together. For example, assume that you and your partner are preparing lunch, you might be chopping vegetables, and they might be preparing sauces. Here both of you can be considered as two threads working on one task together.
With the introduction of multicore CPUs, multithreading has become very common. A lot of modern-day browsers, like Chrome, have multithreaded code written in C++.
Let’s write two functions, one of them will print the letter “A” 200 times and the other will print the letter “B” 200 times. We will call these functions from the main function. Create a C++ file and paste the following code.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<iostream>
void function1() {
for (int i = 0; i < 200; ++i)
std::cout << "A";
}
void function2() {
for (int i = 0; i < 200; ++i)
std::cout << "B";
}
int main() {
function1();
function2();
}
Now, if you run this you will always get the same output.
This was single threaded.
But what if we tell our code to execute both the functions on separate threads? To do that, first we will include the thread library. Include the following header file.
#include<thread>
Now we need to declare two threads, both of them will execute one function. To declare a thread we use the following syntax:
std::thread threadName(functionName);
Here functionName is the name of the function you want to be executed on your thread.
Modify your main function like this.
1
2
3
4
5
6
int main() {
std::thread worker1(function1);
std::thread worker2(function2);
worker1.join();
worker2.join();
}
Here, we have created two threads, worker1 and worker2. In the end we join our threads, otherwise it will create a huge loss of resources if the threads get stuck.
Take a look at the output. Notice the command I have used for compilation, this is for using Linux.
You can see that the A and B are mixed up, clearly showing that both the processes are working together. And running it again will yield different results, because both are running on two cores separately. This is multithreaded.
Passing parameters to functions that are run by threads:
We cannot pass the parameters to the function during its call the regular way, because here the function itself is a parameter. Instead, use the following syntax; modify function1 like this, where it is accepting a parameter.
1
2
3
4
void function1(char c) {
for (int i = 0; i < 200; ++i)
std::cout << c;
}
Now modify the main function like this, where we are calling the function.
1
2
3
4
5
6
int main() {
std::thread worker1(function1, 'o');
std::thread worker2(function2);
worker1.join();
worker2.join();
}
Notice how we are passing the arguments required by function1 directly to the thread constructor of worker1.
Let’s see the output now.
As you can see it is working perfectly.
A real-life example of multithreading:
Let’s say you have an app that displays weather, and you want the app to show it instantly. To get the information about the weather, you are using an API. As getting the data from the API takes some time, this will make our app slower and will not allow us to show data instantly. We can create a thread that runs in the background and refreshes the weather data every hour while another thread can display data instantly, whenever required. However, we are not going to do that here, because calling APIs is beyond the scope of this tutorial.
Thanks for reading about the basics of multithreading in C++. Here is the final complete code.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<iostream>
#include<thread>
void function1(char c) {
for (int i = 0; i < 200; ++i)
std::cout << c;
}
void function2() {
for (int i = 0; i < 200; ++i)
std::cout << "B";
}
int main() {
std::thread worker1(function1, 'o');
std::thread worker2(function2);
worker1.join();
worker2.join();
std::cout << std::endl;
}