必须用 while 循环或带谓词的 wait(),因虚假唤醒是标准允许行为,if 只检查一次会导致条件未满足时继续执行,引发崩溃或数据错乱;cv.wait(lock, pred) 内部即 while 循环,语义清晰且安全。

必须用 while 循环或带谓词的 wait(),否则线程大概率在条件未满足时继续执行,引发崩溃或数据错乱。
为什么 cv.wait() 不能只用 if
虚假唤醒是标准允许的行为,不是 bug。线程可能因信号中断、内核调度抖动或 futex 实现细节被无通知唤醒。if (!ready) cv.wait(lock); 一旦发生虚假唤醒,ready 仍是 false,后续读写直接越界或解引用空指针。
- 每次
wait()返回后,必须重新检查条件是否真成立 -
if只检查一次,while强制重入验证 - 哪怕只写错成
if,在高并发或压力测试下几乎必现
该用 cv.wait(lock, pred) 还是手写 while 循环
推荐优先用带谓词的重载:cv.wait(lock, []{ return !queue.empty(); });。它内部就是展开为 while (!pred()) wait(lock);,语义清晰、不易漏写循环。
- 谓词必须是无副作用的纯检查:用
queue.empty(),别用queue.pop() - 若谓词计算开销大(如遍历查找),手动
while更可控,可提前缓存中间结果 - 禁止混用:别在外层
if调用谓词版wait(),再在返回后又检查一遍——状态可能已变,逻辑错乱
生产者-消费者中容易漏掉的复合条件检查
仅检查 !queue.empty() 不够。真实场景往往需要多个变量共同决定退出等待,比如队列非空 且 未收到关闭信号。
立即学习“C++免费学习笔记(深入)”;
- 谓词必须覆盖所有相关状态:
[&]{ return !queue.empty() && !shutdown_flag.load(std::memory_order_acquire); } - 如果
shutdown_flag是std::atomic<bool></bool>,必须和queue的访问保持内存序一致;若queue由互斥锁保护,则shutdown_flag也应纳入同一锁保护,或确保其 load 使用acquire语义 - 唤醒后、取数据前,仍需在同一个
std::unique_lock下完成front()和pop(),否则其他线程可能已清空队列
notify_one() 和状态更新必须在同一个锁内完成
虚假唤醒本身不可控,但“唤醒后看到旧状态”是可以预防的。关键在通知端的配对逻辑。
- 修改共享状态(如
queue.push(item))前,必须已持有对应std::mutex -
cv.notify_one()必须在**同一锁作用域内**调用,不能先释放锁再通知 - 常见翻车:把
notify_one()放在if分支外,导致通知早于状态写入,或通知发给了尚未进入wait()的线程
最易被忽略的是:谓词返回 true 后、你真正开始操作前,那一小段窗口期仍是竞态高发区——所以“锁 + 检查 + 操作”三步必须原子地落在同一临界区内。


















