Multithreading is a powerful technique that enables a program to perform multiple tasks simultaneously. A thread is a lightweight process that can be executed independently of other threads in a program. Multithreading is useful in scenarios where a program needs to perform multiple tasks at the same time. By dividing the program's tasks into separate threads, the program can make better use of the available system resources and complete tasks more efficiently. In this blog, we will discuss what multithreading is and how to implement multithreading in C++.
What is Multithreading? ๐ค
Multithreading is the ability of a program to execute multiple threads concurrently. A thread is a lightweight process that can be managed independently of other threads in a program. Each thread has its own execution context, including its own stack and program counter, and can run independently of other threads. By dividing a program's tasks into separate threads, the program can make better use of the available system resources and complete tasks more efficiently.
Multithreading is useful in a wide range of applications, including:
Web servers, where multiple threads can handle incoming requests concurrently
Gaming, where one thread can handle graphics processing while another thread handles game logic
Video editing, where multiple threads can be used to speed up rendering and processing
Operating systems, where multiple threads can handle different system tasks concurrently
Implementing Multithreading in C++ C++ provides support for multithreading through the use of the Standard Template Library (STL) threading library. The threading library provides classes and functions that can be used to create and manage threads in a C++ program.
To use the threading library in C++, we need to include the <thread> header file. The following is an example program that demonstrates how to create and manage threads in C++.
#include <iostream>
#include <thread>
using namespace std;
// A function that runs in a separate thread
void threadFunction(int n)
{
for(int i = 0; i < n; i++)
{
cout << "Thread function executing" << endl;
}
}
int main()
{
// Create a thread and start running threadFunction
thread t(threadFunction, 10);
// Wait for the thread to finish
t.join();
// The main thread continues to execute after the other thread has finished
cout << "Main function executing" << endl;
return 0;
}
In this program, we define a function called threadFunction that takes an integer argument n. This function will be run in a separate thread.
We then create a thread object called t and pass it the threadFunction and the argument 10. This starts the thread running the threadFunction.
We then call the join() method on the thread object t. This waits for the thread to finish before continuing execution in the main thread.
Finally, we print a message to the console indicating that the main function is executing.
When we compile and run this program, we should see the following output:
Thread function executing
Thread function executing
Thread function executing
Thread function executing
Thread function executing
Thread function executing
Thread function executing
Thread function executing
Thread function executing
Thread function executing
Main function executing
As we can see, the threadFunction is executed ten times, and then the main function is executed.
Creating Threads in C++
To create a thread in C++, we need to create a thread object and pass it a function to run in the new thread. The following is an example program that demonstrates how to create a thread in C++.
#include <iostream>
#include <thread>
using namespace std;
// A function that runs in a separate thread
void threadFunction()
{
cout << "Thread function executing" << endl;
}
int main()
{
// Create a thread and start running threadFunction
thread t(threadFunction);
// Wait for the thread to finish
t.join();
// The main thread continues to execute after the other thread has finished
cout << "Main function executing" << endl;
return 0;
}
In this program, we define a function called threadFunction that simply prints a message to the console.
We then create a thread object called t and pass it the threadFunction. This starts the thread running the threadFunction.
We then call the join() method on the thread object t. This waits for the thread to finish before continuing execution in the main thread.
Finally, we print a message to the console indicating that the main function is executing.
When we compile and run this program, we should see the following output:
Thread function executing
Main function executing
As we can see, the threadFunction is executed first, and then the main function is executed.
Passing Arguments to Thread Functions
We can also pass arguments to the thread function by passing them as additional arguments to the thread constructor. The following is an example program that demonstrates how to pass an integer argument to a thread function.
#include <iostream>
#include <thread>
using namespace std;
// A function that runs in a separate thread
void threadFunction(int n)
{
for(int i = 0; i < n; i++)
{
cout << "Thread function executing" << endl;
}
}
int main()
{
// Create a thread and start running threadFunction with argument 5
thread t(threadFunction, 5);
// Wait for the thread to finish
t.join();
// The main thread continues to execute after the other thread has finished
cout << "Main function executing" << endl;
return 0;
}
In this program, we modify the threadFunction to take an integer argument n. We then pass the value 5 as an argument to the thread constructor when we create the thread object.
When we run this program, we should see the following output:
Thread function executing
Thread function executing
Thread function executing
Thread function executing
Thread function executing
Main function executing
As we can see, the threadFunction is executed five times, and then the main function is executed.
Synchronizing Threads
When we create multiple threads, it is often necessary to synchronize their execution to ensure that they do not interfere with each other. One way to synchronize threads is through the use of mutexes.
A mutex is a synchronization object that allows multiple threads to share a resource without interfering with each other. A mutex can be in one of two states: locked or unlocked. When a thread acquires a lock on a mutex, no other thread can acquire a lock on that mutex until the first thread releases the lock.
The following is an example program that demonstrates how to use a mutex to synchronize two threads.
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;
mutex myMutex;
// A function that runs in a separate thread
void threadFunction()
{
myMutex.lock();
cout << "Thread function executing" << endl;
myMutex.unlock();
}
int main()
{
// Create two threads and start running threadFunction
thread t1(threadFunction);
thread t2(threadFunction);
// Wait for both threads to finish
t1.join();
t2.join();
// The main thread continues to execute after both other threads have finished
cout << "Main function executing" << endl;
return 0;
}
In this program, we create a mutex object called myMutex. We then modify the threadFunction to acquire the lock on the mutex before printing the message and release the lock after printing the message.
We then create two thread objects called t1 and t2, and pass them the threadFunction. Both threads will attempt to acquire the lock on the mutex before printing the message.
We then call the join() method on both thread objects to wait for both threads to finish executing.
Finally, we print a message to the console indicating that the main function is executing.
When we compile and run this program, we should see the following output:
Thread function executing
Thread function executing
Main function executing
As we can see, both threads execute the threadFunction and print the message in a synchronized manner, without interfering with each other. This ensures that the output is predictable and consistent.
Conclusion ๐
Multithreading is a powerful technique that can greatly improve the performance and responsiveness of our applications. By executing multiple threads simultaneously, we can make our applications more efficient and scalable.
In C++, we can create threads using the std::thread class, and synchronize their execution using mutexes and other synchronization objects.
It's important to keep in mind, however, that multithreading can also introduce new challenges, such as race conditions and deadlocks. Therefore, it's important to use multithreading judiciously and with caution, and to thoroughly test our code to ensure its correctness and robustness.