std::condition_variable 必须与 std::mutex 和共享布尔条件变量配合使用,wait() 需传 unique_lock 以原子执行“解锁-挂起-重锁”,必须用 lambda 循环检查条件,notify_one/notify_all 须按场景慎选,且所有条件读写必须加锁。

直接说结论:std::condition_variable 本身不能单独使用,必须搭配 std::mutex 和一个**共享的布尔条件变量**(比如 bool ready),否则极易陷入虚假唤醒或死锁。
为什么 wait() 必须传入 unique_lock
std::condition_variable::wait() 设计上要求传入 std::unique_lock<:mutex>,不是因为“语法强制”,而是为了解决「原子性释放锁 + 等待」这个关键动作:
- 线程在进入等待前,必须先释放锁,否则其他线程无法修改条件;
- 但释放锁和挂起自己之间不能有间隙,否则可能错过通知(notify);
-
wait()内部会自动完成「解锁 → 挂起 → 被唤醒后重新加锁」这一整套原子操作。
传 std::lock_guard 会编译失败,因为 lock_guard 不支持中途解锁;传裸 mutex.lock()/unlock() 则无法保证原子性,属于典型误用。
wait() 的 lambda 条件判断不是可选的,是必须的
别写 cv.wait(lock) 这种无条件等待——它不检查条件是否成立,只等通知,但通知可能早于条件就绪(spurious wakeup),也可能被多个 notify 堆叠导致逻辑错乱。
立即学习“C++免费学习笔记(深入)”;
正确写法永远是:
cv.wait(lock, [&ready] { return ready; });
这等价于:
while (!ready) {
cv.wait(lock);
}
常见错误包括:
- 用
if (!ready) cv.wait(...)—— 只检查一次,漏掉虚假唤醒; - 把条件判断写成
cv.wait(lock, []{ return false; })—— 永远阻塞; - 条件变量未用
volatile或原子类型保护(其实不用,但读写必须在锁内)。
notify_one() 和 notify_all() 的实际影响差异很大
多数教程轻描淡写说“一个唤醒一个,一个唤醒全部”,但真实场景中选错会导致性能崩塌或逻辑错误:
-
notify_one():适用于「一个任务对应一个等待者」,比如生产者-消费者中单个 item 入队,只唤醒一个消费者; -
notify_all():适用于「条件变更影响所有等待者」,比如关闭标志shutdown = true,所有线程都该退出; - 误用
notify_one()在多消费者场景下,可能导致部分线程永久挂起(没人再通知它); - 滥用
notify_all()在高并发下会引发“惊群效应”,几十个线程同时争抢锁,CPU 白耗在上下文切换上。
condition_variable 不是万能的,别在循环里空等条件
有人试图用 condition_variable 实现超时轮询、状态轮检,这是反模式。例如:
// ❌ 错误:用 condition_variable 等待某个非同步事件发生(如文件生成)
while (!file_exists("data.txt")) {
std::this_thread::sleep_for(100ms); // 应该用 filesystem + inotify,不是 cv
}
真正适合 std::condition_variable 的场景只有两类:
- 线程间明确的协作信号(A 完成后通知 B);
- 资源就绪通知(缓冲区非空 / 非满)。
它不提供时间感知、事件源绑定、跨进程通信能力——这些得靠 std::chrono、inotify、boost::interprocess 等补足。
最常被忽略的一点:所有对共享条件的读写,哪怕只是 ready = true,也必须发生在持有同一把 std::mutex 的前提下。漏掉这个保护,即使用了 condition_variable,也是数据竞争。









