After this lesson, you will be able to: Use std::thread, std::mutex, std::atomic, std::async, std::future for safe concurrent C++.
Concurrent C++ is hard. Get it right and you have a fast multi-core program. Get it wrong and you have data races, deadlocks, and bugs that only show up under load.
Spawn a thread; join before it goes out of scope.
#include <thread>#include <iostream>void work(int id) {std::cout << "thread " << id << "\n";}int main() {std::thread t1(work, 1);std::thread t2(work, 2);t1.join(); // wait for t1 to finisht2.join(); // wait for t2 to finish// forgetting to join = std::terminate at scope exit}// C++20: std::jthread auto-joins on destruction// std::jthread t(work, 1); // no manual join needed
Protect every shared variable with a mutex.
#include <mutex>#include <thread>#include <vector>int counter = 0;std::mutex m;void increment() {for (int i = 0; i < 1000; ++i) {std::lock_guard<std::mutex> lock(m); // RAII lock++counter; // critical section} // lock auto-releases here}int main() {std::vector<std::thread> ts;for (int i = 0; i < 10; ++i) ts.emplace_back(increment);for (auto& t : ts) t.join();std::cout << counter << "\n"; // 10000, correct}// Without the lock, counter is undefined, data race.// C++20: std::scoped_lock for multiple mutexes (deadlock-free)
Faster than mutex for simple types.
#include <atomic>std::atomic<int> counter{0};void increment() {for (int i = 0; i < 1000; ++i) {counter.fetch_add(1, std::memory_order_relaxed);// shorthand: ++counter;}}
Run a function async; get its return value when ready.
#include <future>#include <iostream>int slowCompute(int x) {std::this_thread::sleep_for(std::chrono::seconds(1));return x * 2;}int main() {std::future<int> f = std::async(std::launch::async, slowCompute, 21);// do other work...int result = f.get(); // blocks until readystd::cout << result << "\n"; // 42}
Forgetting to join (terminate at scope exit). Use jthread (C++20) or RAII wrappers. Forgetting `noexcept` on move operations of mutex-holding classes. Raw `m.lock()` + `m.unlock()` (exception = held lock = deadlock). Double-locking a non-recursive mutex from the same thread. Sharing a non-atomic int across threads (data race = UB).
Sign in and purchase access to unlock this lesson.