cv.wait(lock, pred)是唯一推荐写法,因其内部自动实现while(!pred())循环,避免虚假唤醒导致的逻辑错误;谓词须覆盖完整业务条件、无副作用、变量状态一致可见。

必须用 cv.wait(lock, pred),不能只写 cv.wait(lock) 加 if 判断——后者在真实并发场景中几乎必然出错。
为什么 cv.wait(lock, pred) 是唯一推荐的写法
这个重载内部就是帮你包了一层 while (!pred()),语义上等价于手写循环,但更简洁、不易漏。它不省去你对谓词逻辑的责任,只是把“反复检查”这件事标准化了。
常见错误是以为用了谓词就万事大吉,结果谓词本身访问了未受同一 std::mutex 保护的变量,比如:
-
queue用mtx保护,但shutdown_flag是独立的std::atomic,谓词里只检查!queue.empty(),没合并在一个原子判断里 - 谓词返回
true后,临界区还没进入,另一个线程已把queue清空或置为finished
所以谓词必须表达完整业务条件,例如生产者-消费者中应写成 [] { return !queue.empty() || finished; },而不是只看队列。
cv.wait_for 和 cv.wait_until 的谓词写法一样重要
带超时的等待函数同样会遭遇虚假唤醒,且多一层超时分支,更容易误判状态。它们返回 bool:true 表示谓词为真(条件满足),false 表示超时或被虚假唤醒——但你无法从返回值区分后两者。
所以依然要靠谓词本身兜底:
- 不要写
if (cv.wait_for(lock, 100ms)) { process(); }就完事 - 必须确保谓词覆盖所有合法退出路径,比如
[] { return !queue.empty() || shutdown_requested.load(); } - 超时返回 false 后,若还想继续等,得自己控制重试逻辑,不能依赖谓词自动重入
谓词必须是无副作用、可重入的纯检查
谓词会在每次唤醒后立即执行,可能被调用多次。如果里面写了 queue.pop() 或修改了共享状态,会导致重复操作或崩溃。
正确做法是把检查和操作严格分离:
- 谓词只做读操作:
queue.empty()、data_ready.load()、counter > 0 - 真正取数据或变更状态,必须放在
cv.wait(...)返回之后,在同一个std::unique_lock保护下进行 - 避免在谓词里调用可能阻塞、抛异常或改变全局状态的函数
最易被忽略的一点:谓词里的所有变量,必须确保在被检查时处于一致、可见的状态——这意味着要么全由同一互斥锁保护,要么是 std::atomic 且 memory order 正确(如 memory_order_acquire)。否则即使写了 while 或用了谓词重载,也拦不住竞态。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











