wait必须与std::unique_lock配合使用,因其实现依赖其自动解锁/重锁机制;notify_one通知不积压,需用while循环加谓词防御虚假唤醒和通知丢失;condition_variable本身不保存状态。

wait 必须和 unique_lock 一起用,不能裸调用
直接传 std::mutex 给 wait() 会编译失败——它只接受 std::unique_lock<:mutex></:mutex>。这是因为 wait() 内部要先解锁、挂起线程,等唤醒后再重新加锁,这个“自动解锁/重锁”逻辑依赖 unique_lock 的可移动性和对锁状态的精确控制。
常见错误写法:cv.wait(mutex)(报错)或 cv.wait(std::lock_guard<:mutex>(mtx))</:mutex>(语法错且语义错)。
正确做法:
- 声明
std::mutex mtx和std::condition_variable cv - 在等待前用
std::unique_lock<:mutex> lk(mtx)</:mutex>构造锁对象 - 调用
cv.wait(lk, []{ return condition; })—— 谓词必须是可调用对象,推荐 lambda
notify_one 不保证唤醒“刚睡着”的那个线程
notify_one() 只唤醒一个正在 wait() 的线程,但不指定是哪一个;更重要的是:如果调用时没有线程在 wait,通知就丢弃了,不会积压。这意味着“先 notify 后 wait”大概率失效。
立即学习“C++免费学习笔记(深入)”;
典型陷阱场景:
- 生产者线程修改共享数据后调用
cv.notify_one() - 消费者线程还没来得及执行
cv.wait(),通知已发出 → 消费者永远阻塞
解决办法:把条件判断放进谓词里,用 while 循环包裹 wait(即所谓的“spurious wakeup 防御”),例如:
while (!data_ready) {
cv.wait(lk);
}
这样即使通知丢失或发生虚假唤醒,循环也会重新检查条件。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
wait 的谓词参数不是可选的,它是安全关键
虽然 wait(unique_lock&) 有无谓词的重载,但**绝对不要用**。裸 wait 容易被虚假唤醒(spurious wakeup)导致逻辑错乱,而且无法表达“等什么条件成立”。
必须用带谓词的版本:wait(lk, []{ return ...; })。谓词返回 true 才退出 wait,false 则自动继续等待并重新加锁。
注意点:
- 谓词中访问的共享变量(如
data_ready)必须受同一 mutex 保护 - 谓词不能抛异常,否则
wait()行为未定义 - 避免在谓词里做耗时操作(比如 IO、复杂计算),它可能被反复调用
notify_one 和 notify_all 的选择取决于等待者语义
如果只有一个消费者在等某件事(比如队列非空),用 notify_one() 更高效;如果有多个线程在等同一个条件,但只需一个处理(比如线程池取任务),也用 notify_one()。
只有当所有等待者都需要被唤醒响应同一事件时,才用 notify_all(),例如:
- 关闭信号广播(所有工作线程需同时检查退出标志)
- 条件变化影响全部等待者(如全局配置重载完成)
滥用 notify_all() 会导致“惊群效应”,尤其在线程数多时明显拖慢性能。
另外:notify_one() 和 notify_all() 不需要持有锁,但通常建议在释放锁**之前**调用,以减少唤醒后线程争抢锁的延迟。
真正容易被忽略的是:condition_variable 本身不保存状态,它只是协调工具;所有业务逻辑的“条件”都得靠你自己用变量 + mutex 维护清楚。

















