condition_variable 必须与 std::unique_lock 配合使用,因其 wait() 需自动释放并重获锁;需用 while 循环防虚假唤醒,notify_one/notify_all 依等待者数量选择,带超时等待须检查返回值确保状态正确。

condition_variable 必须和 unique_lock 配合使用
直接用 std::mutex 加锁后调用 wait() 会编译失败——condition_variable::wait() 只接受 std::unique_lock<:mutex></:mutex>,不支持 std::lock_guard 或裸 mutex.lock()。这是因为 wait() 内部需要在阻塞前自动释放锁,并在唤醒后重新获取,而只有 unique_lock 支持可移动、可手动解锁/重锁。
常见错误写法:
std::mutex mtx;
std::condition_variable cv;
std::lock_guard<:mutex> lk(mtx); // 错!不能传给 cv.wait()
cv.wait(lk, []{ return ready; });
</:mutex>
正确写法要点:
-
unique_lock构造时传入mutex,默认自动加锁 -
wait()第二个参数是谓词(lambda 或函数对象),必须返回bool;它会在每次被唤醒时重新检查,避免虚假唤醒 - 不要手动调用
unlock()再wait()——wait()自己会处理锁的释放与重获
notify_one() 和 notify_all() 的选择要看等待者语义
用错通知方式是线程同步 bug 的高发区。如果只有一个消费者在等某个条件(比如一个任务队列只由一个工作线程消费),用 notify_one() 更高效;但若多个线程在等同一条件(比如多个线程等“资源池已就绪”),必须用 notify_all(),否则可能遗漏唤醒。
典型陷阱:
- 生产者只调
notify_one(),但消费者有多个且都卡在wait()上 → 其余消费者永远等不到信号 - 生产者频繁调
notify_all(),但实际只有一项新数据 → 所有等待线程被唤醒、抢锁、检查条件失败、再挂起,浪费 CPU(即“惊群效应”) -
notify_*调用本身不加锁,但必须确保通知前已完成对共享状态的修改(如设置ready = true),且该修改对等待线程可见(通常靠 mutex 保护)
虚假唤醒(spurious wakeup)必须用 while 循环检查条件
POSIX 和 C++ 标准都允许 condition_variable::wait() 在没有对应 notify 的情况下返回。这不是 bug,是实现优化导致的合法行为。所以绝对不能用 if 判断条件后 wait(),必须用 while 循环重检。
错误示例:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
if (!data_ready) {
cv.wait(lk);
}
// data_ready 仍可能为 false!
正确写法:
while (!data_ready) {
cv.wait(lk);
}
// 此时 data_ready 必然为 true(前提是你在 notify 前已设为 true 并处于同一 mutex 保护下)
这也是为什么 wait() 接受谓词:它内部就是帮你展开成等价的 while 循环。
wait_for / wait_until 要小心超时后的状态判断
带超时的等待(wait_for()、wait_until())返回的是 std::cv_status 枚举,或谓词返回值(当用谓词重载时)。很多人忽略超时后条件仍可能未满足,直接往下走逻辑,导致读到脏数据或执行错误分支。
关键点:
- 用谓词版本(如
cv.wait_for(lk, 100ms, []{return done;}))最安全:它只在谓词为true时返回true,超时则返回false - 若用无谓词版本,必须检查返回值:
cv.wait_for(lk, 100ms) == std::cv_status::no_timeout才表示是被 notify 唤醒的 - 即使超时返回,
unique_lock依然持有锁,需自行决定是否继续等待、退出或报错
超时不是异常,而是同步流程的一部分;没处理好,就变成“有时快有时卡死”的偶发问题。
condition_variable 的难点不在语法,而在对“谁改状态、谁等状态、谁发通知、通知几次、超时怎么兜底”这整条链路的精确控制。漏掉任意一环,线程就可能永久阻塞、错过信号或重复处理。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










