std::condition_variable必须与std::mutex配合使用,所有操作须在持有同一把锁的前提下进行;wait()需用while循环防虚假唤醒;notify_one()比notify_all()更高效,适用于典型生产者-消费者场景。

std::condition_variable 必须和 std::mutex 配合使用
单独声明 std::condition_variable 没有意义,它不保存状态,也不提供原子操作。所有 wait()、notify_one()、notify_all() 调用都必须在持有同一把 std::mutex 的前提下进行,否则行为未定义(常见崩溃或死锁)。
典型错误是:在 wait() 前没加锁,或在 notify_xxx() 时没锁、或用了不同 mutex —— 这些都会导致程序随机失败,尤其在多核机器上更难复现。
-
wait()会自动释放传入的std::unique_lock<:mutex></:mutex>,并在被唤醒后重新获取锁,所以必须传入已锁定的锁对象 -
notify_one()和notify_all()不要求当前线程持有锁,但为避免竞态(例如通知时消费者刚检查完条件但还没 wait),**强烈建议在持有锁的上下文中调用** - 不要用
std::lock_guard替代std::unique_lock:前者不可转移、不可手动解锁,无法满足wait()的内部解锁需求
必须用 while 循环检查条件,不能用 if
std::condition_variable::wait() 可能被虚假唤醒(spurious wakeup),即没有被 notify 就返回。C++ 标准允许这种行为,且各平台实现均存在。用 if 判断一次就进入临界区,会导致逻辑错乱(比如从空队列取数据)。
正确做法是把条件判断写在 while 循环中,确保每次从 wait() 返回后都重新验证业务条件是否真正满足。
立即学习“C++免费学习笔记(深入)”;
- 错误写法:
if (queue.empty()) cv.wait(lock); - 正确写法:
while (queue.empty()) cv.wait(lock); - 生产者同理:用
while (queue.size() >= capacity)判断是否满,而非if
一个可运行的双线程生产者-消费者实例
以下代码用一个固定容量的 std::queue 模拟缓冲区,两个线程分别执行生产和消费,共享一个 std::mutex 和一个 std::condition_variable。注意:所有对 queue 的读写都受同一把锁保护,cv 仅用于阻塞/唤醒协调。
#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
std::queue<int> buffer;
std::mutex mtx;
std::condition_variable cv_producer, cv_consumer;
const size_t CAPACITY = 3;
void producer() {
for (int i = 0; i < 10; ++i) {
std::unique_lock<std::mutex> lock(mtx);
while (buffer.size() == CAPACITY) {
cv_producer.wait(lock); // 等待有空位
}
buffer.push(i);
std::cout << "Produced: " << i << "\n";
lock.unlock(); // 手动解锁,避免 notify 时还持锁(非必须,但更清晰)
cv_consumer.notify_one(); // 唤醒一个消费者
}
}
void consumer() {
for (int i = 0; i < 10; ++i) {
std::unique_lock<std::mutex> lock(mtx);
while (buffer.empty()) {
cv_consumer.wait(lock); // 等待有数据
}
int val = buffer.front();
buffer.pop();
std::cout << "Consumed: " << val << "\n";
lock.unlock();
cv_producer.notify_one(); // 唤醒一个生产者
}
}
int main() {
std::thread t1(producer);
std::thread t2(consumer);
t1.join();
t2.join();
return 0;
}
notify_one() vs notify_all() 的实际影响
在生产者-消费者模型中,通常用 notify_one() 就够了:一个新元素入队,只需唤醒一个等待的消费者;一个元素出队,只需唤醒一个等待的生产者。滥用 notify_all() 会造成“惊群效应”——多个线程同时被唤醒、竞争锁、大部分又立刻回到等待,浪费 CPU 和调度开销。
只有在以下情况才考虑 notify_all():
- 条件变量关联多个互斥条件(比如同时等待“非空”或“非满”)
- 无法确定哪个等待线程的条件已满足(如优先级队列场景)
- 使用
wait_for()或wait_until()且需统一处理超时后的批量清理
多数简单队列场景里,notify_one() 更轻量、更可控。但要注意:如果唤醒的线程因条件不满足又立即 wait,而其他线程本可推进,说明逻辑或唤醒时机有问题 —— 这类问题往往藏在循环条件或 notify 位置里。











