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
#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';
}100 jobs, total 5050Lock
The waiter takes a
std::unique_lockon the mutex that protects the shared state.Wait with a predicate
cv.wait(lock, pred)checkspred; if false it atomically unlocks and sleeps, then relocks when woken and re-checks.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?