C++ Threading

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.

Creating Threads

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;
}
    

Joining Threads

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.

Passing Arguments to Threads

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;
}
    

Synchronization

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).

Example with Mutex

#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.