Skip to content
  • Condition Variables & Producer–Consumer · C++

Condition Variables & Producer–Consumer

LearnViaNotes @learnvianotes· 2 min read

A std::condition_variable lets a thread sleep until another thread says “something changed”. Combined with a mutex and a queue it gives the classic producer–consumer pattern.

A thread-safe queue

C++
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <optional>
#include <queue>
#include <thread>

template <typename T>
class BlockingQueue {
    std::queue<T> q_;
    std::mutex m_;
    std::condition_variable cv_;
    bool closed_ = false;
public:
    void push(T v) {
        { std::lock_guard lock(m_); q_.push(std::move(v)); }
        cv_.notify_one();                                     // wake one waiting consumer
    }
    void close() {
        { std::lock_guard lock(m_); closed_ = true; }
        cv_.notify_all();
    }
    std::optional<T> pop() {
        std::unique_lock lock(m_);
        cv_.wait(lock, [&] { return !q_.empty() || closed_; });   // predicate guards spurious wake-ups
        if (q_.empty()) return std::nullopt;                   // closed and drained
        T v = std::move(q_.front());
        q_.pop();
        return v;
    }
};

int main() {
    BlockingQueue<int> jobs;
    long long total = 0;
    int handled = 0;
    {
        std::jthread consumer([&] {
            while (auto job = jobs.pop()) { total += *job; ++handled; }
        });
        std::jthread producer([&] {
            for (int i = 1; i <= 100; ++i) jobs.push(i);
            jobs.close();
        });
    }
    std::cout << handled << " jobs, total " << total << '\n';
}
Output
100 jobs, total 5050
  1. Lock

    The waiter takes a std::unique_lock on the mutex that protects the shared state.

  2. Wait with a predicate

    cv.wait(lock, pred) checks pred; if false it atomically unlocks and sleeps, then relocks when woken and re-checks.

  3. Modify, then notify

    The producer changes the state under the mutex, releases it, and calls notify_one() / notify_all().

Why does condition_variable::wait need a unique_lock rather than a lock_guard?

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report