C++ Concurrency in Action¶
Introduction¶
<thread>¶
Basic Thread Management¶
Launching a Thread¶
C++ Thread Constructor
template <class F, class... Args>
explicit thread(F&& f, Args&&... args);
thread(threads&& other) noexcept; // Move constructor
other: another thread object to construct this thread object withf: Callable object to execute in the new threadargs: Arguments to pass to the new function
Common Pitfall
C++ interprets ambiguous syntax as function declaration!
Recommended
Use uniform initialization {} or lambdas to avoid parsing issues.
Waiting for a Thread to Complete¶
join(): Wait for thread completion before continuing.
safe thread management with RAII:
class thread_guard {
thread& t;
public:
explicit thread_guard(thread& t_) : t(t_) {}
~thread_guard() { if (t.joinable()) t.join(); }
thread_guard(const thread_guard&) = delete; // 禁止拷贝
thread_guard& operator=(const thread_guard&) = delete; // 禁止赋值
void do_work() {
thread t([]() { /* do work */ });
thread_guard g(t); // RAII: 自动 join
risky_operation(); // Safe!
// Deconstrutor ensures thread is joined
}
};
Running Threads in the Background¶
detach(): Run the thread independently without waiting for it to finish.
Important
Every thread must be joined or detached before destruction.
Passing Arguments to a Thread Function¶
Parameter Passing Basics¶
Key Principle: Thread constructor copies arguments as rvalues.
Transferring Ownership of a Thread¶
Thread is move-only, meaning it cannot be copied.
Choosing the Number of Threads at Runtime¶
<mutex>¶
Race Conditions¶
The core issue: Multiple threads accessing shared data
int counter = 0; // Shared variable
void increment_unsafe(int iterations) {
for (int i = 0; i < iterations; i++) {
counter++; // NOT thread-safe!
}
}
// 4 threads, each incrementing 100000 times
std::vector<std::thread> threads;
for (int i = 0; i < 4; i++) {
threads.emplace_back(increment_unsafe, 100000);
}
for(auto& t : threads) { t.join(); }
// Expected: 400000, Actual: varies (e.g., 387234)
Assembly breakdown of counter++:
Thread Interleaving Problem:
- Thread 1 reads
counter = 0 - Thread 2 reads
counter = 0 - Thread 1 writes
counter = 1 - Thread 2 writes
counter = 1 - Lost update! Should be
counter = 2
Critical Section: Code accessing shared resources
Protecting Shared Data with Mutexes¶
C++ Mutex Solution¶
Using std::mutex with RAII:
<condition_variable>¶
Waiting for an Event or Other Condition¶
Inefficient waiting with sleep_for:
- CPU wastage from constant polling
- Unnecessary latency, unpredictable timing
Waiting for a Condition with Condition Variables¶
Condition Variable Types¶
std::condition_variable: Works only withstd::mutexstd::condition_variable_any: Works with any mutex type (extra overhead)
wait() overloads¶
// Version 1: Basic wait
void wait (std::unique_lock<std::mutex>& lock);
// Version 2: Wait with predicate (recommended)
void wait (std::unique_lock<std::mutex>& lock, Predicate pred);