Skip to main content

Mutexes, Condition Variables, Futures

advanced11 min readLesson 164 of 204

Protecting shared state, waking sleepers, and one-shot hand-offs โ€” the three coordination tools.

Mutex + RAII guards

std::mutex with std::lock_guard (scope-locked) or std::unique_lock (movable, conditional). The rule: guard construction owns the lock; never call the mutex directly when a guard exists. Shared-read-heavy data wants std::shared_mutex with shared_lock readers / unique_lock writers.

Condition variables: wait for a state change

std::mutex m;
std::condition_variable cv;
bool ready = false;

// waiter
std::unique_lock lk(m);
cv.wait(lk, [] { return ready; });   // releases m while waiting; re-acquires before returning

// notifier
{ std::lock_guard lk(m); ready = true; }
cv.notify_one();

The predicate form is not optional polish โ€” it guards against lost wakeups and spurious wakeups. Every wait must be predicated; every state change must hold the mutex and notify.

std::future / std::promise: one-shot hand-off

std::promise is the write end, std::future the read end of a single value transfer โ€” including exceptions:

std::promise<int> p;
std::future<int> f = p.get_future();
std::thread t([&p] { p.set_value(compute()); });
int result = f.get();   // blocks until set โ€” or rethrows the worker's exception
t.join();

std::async wraps this; std::packaged_task wraps a callable. These are for one-shot results; a queue of tasks is the thread pool's job (next lesson).

Now practice

Coordination PrimitivesThe predicated condition-variable handoff and shared-state discipline.1 challenge ยท ยท ~18 min