C++C++ Explainer
C++ · 6 · Concurrency26 / 36 · 72%

condition_variable & Producer/Consumer

Wait for a condition without burning CPU on a spin loop.

Examples: unique_lock, wait, notify_one

shortcuts: ← prev · → next · M mark
1

Wait With a Predicate

Always pass the predicate overload — it handles spurious wakeups for you.

Example
example
std::mutex m;
std::condition_variable cv;
std::queue<Job> q;

// consumer
std::unique_lock lk(m);
cv.wait(lk, [&]{ return !q.empty() || done; });
Job j = std::move(q.front()); q.pop();
WATCH OUT
cv.wait(lk) without a predicate can wake up spuriously and proceed with no data. Never use the bare form.
2

Notify

Modify the shared state under the lock, then notify.

Example
example
{
    std::lock_guard lk(m);
    q.push(std::move(job));
}
cv.notify_one();      // notify_all() to wake every waiter
3

A Thread-Safe Queue

The canonical interview implementation.

Example
example
template <typename T>
class Queue {
    std::mutex m_; std::condition_variable cv_;
    std::queue<T> q_; bool closed_ = false;
public:
    void push(T v) {
        { std::lock_guard lk(m_); q_.push(std::move(v)); }
        cv_.notify_one();
    }
    bool pop(T& out) {
        std::unique_lock lk(m_);
        cv_.wait(lk, [&]{ return !q_.empty() || closed_; });
        if (q_.empty()) return false;
        out = std::move(q_.front()); q_.pop();
        return true;
    }
    void close() {
        { std::lock_guard lk(m_); closed_ = true; }
        cv_.notify_all();
    }
};
4

Deadlock Rules

Two locks, two orders, one hang.

Common pitfalls
  • ✕Always acquire multiple mutexes in the same global order, or use std::scoped_lock(a, b).
  • ✕Never call user code or a callback while holding a lock.
  • ✕Never notify while holding the lock for long — unlock first where you can.
  • ✕A lost wakeup happens if you notify before the waiter registers; the predicate form prevents it.