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