Modern C++ (C++11 and later) provides built-in support for threading, allowing you to execute multiple parts of your program concurrently. This can significantly improve performance, especially on multi-core processors.
The std::thread
class is used to create and manage threads. You pass a function (or any callable object) to the std::thread
constructor, which will be executed in a new thread.
#include <iostream>
#include <thread>
void myFunction() {
std::cout << "Hello from a thread!" << std::endl;
}
int main() {
std::thread myThread(myFunction); // Create a thread
// ... do other work ...
myThread.join(); // Wait for the thread to finish
return 0;
}
The join()
method is crucial. It blocks the calling thread until the std::thread
it's called on completes its execution. Failing to join a thread can lead to program termination or undefined behavior.
You can pass arguments to the function executed by a thread. Be careful with passing by reference; you might need std::ref
to ensure the argument is passed by reference and not by value.
void myFunction(int arg) {
std::cout << "Thread received: " << arg << std::endl;
}
int main() {
int value = 42;
std::thread myThread(myFunction, value); // Pass by value
int refValue = 100;
std::thread refThread(myFunction, std::ref(refValue)); // Pass by reference
myThread.join();
refThread.join();
return 0;
}
When multiple threads access shared resources, you need to synchronize their access to prevent race conditions and data corruption. C++ provides various synchronization primitives like mutexes (std::mutex
, std::recursive_mutex
), condition variables (std::condition_variable
), and atomic variables (std::atomic
).
#include <iostream>
#include <thread>
#include <mutex>
std::mutex myMutex;
int sharedValue = 0;
void increment() {
for (int i = 0; i < 10000; ++i) {
std::lock_guard<std::mutex> lock(myMutex); // Lock the mutex
sharedValue++;
}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
std::cout << "Shared Value: " << sharedValue << std::endl; // Should be 20000
return 0;
}
Threading is a powerful tool, but it also introduces complexity. Careful design and proper synchronization are essential to avoid common pitfalls like deadlocks and race conditions.