std::condition_variable必须与std::mutex配合使用,且只能用std::unique_lock包裹;等待必须在while循环中通过谓词检查条件,以应对虚假唤醒和通知丢失;notify_one()和notify_all()需按场景谨慎选择,且通知不保存状态。

std::condition_variable 必须和 std::mutex 一起用
单独声明 std::condition_variable 没法工作,它不管理共享状态,只负责“通知”和“等待”——而等待时必须持有锁,否则会触发未定义行为。常见错误是漏掉 std::unique_lock<:mutex></:mutex>,或者用 std::lock_guard(它不能转移所有权,wait() 需要能临时释放并重新获取锁)。
- 必须用
std::unique_lock包裹std::mutex,传给wait()或wait_for() - 条件检查必须在
wait()的 lambda 或谓词中完成,不能在外面“先判断再 wait”,否则有竞态 -
notify_one()不保证唤醒成功:如果此时没人正在wait(),通知就丢掉了;需要靠循环条件 + 谓词兜底
虚假唤醒(spurious wakeup)不是 bug,是设计事实
即使没人调用 notify_one() 或 notify_all(),wait() 也可能返回。这是 POSIX 和 C++ 标准允许的行为,硬件中断、调度器优化都可能导致。所以永远不要写 if (flag) { wait(); },而要用 while (!flag) { wait(); }。
- 所有
wait()调用必须包裹在while循环里,检查真实业务条件 - 别试图“避免”虚假唤醒——它无法禁用,只能正确应对
- 使用带谓词的
wait(lock, pred)等价于while (!pred()) wait(lock);,更简洁且不易错
notify_one() vs notify_all():选错会影响性能和逻辑
notify_one() 只唤醒一个等待线程,适合“生产者-消费者”中每次只处理一个任务的场景;notify_all() 唤醒全部,适合多个线程在等同一个状态变更(比如资源初始化完成)。但后者容易引发“惊群”,尤其在线程数多时,所有被唤醒线程都要竞争锁、重检条件,造成不必要的上下文切换。
- 优先用
notify_one(),除非你明确需要唤醒所有等待者 - 用
notify_all()时,确保等待线程的条件检查足够轻量,否则性能会明显下降 - 注意:
notify_*可以在锁外调用,但若通知后立即修改共享状态,建议锁内通知,避免唤醒后立刻又得等
wait_for() 超时后,条件变量本身不报错,但你要自己处理超时路径
wait_for() 返回的是 std::cv_status 枚举,不是布尔值。常见错误是直接用 if (cv.wait_for(...)) 判断,结果编译不过或逻辑翻车。它返回 std::cv_status::no_timeout 表示被通知唤醒,std::cv_status::timeout 才是超时。
立即学习“C++免费学习笔记(深入)”;
- 必须显式比较返回值:
if (cv.wait_for(lk, 100ms) == std::cv_status::timeout) - 超时后,仍需检查业务条件是否满足(因为可能刚超时,另一个线程就改了状态)
- 不要依赖超时来“替代”条件检查——超时只是兜底,不是同步逻辑的主干
最常被忽略的是:条件变量不保存状态,也不记录通知次数。一次 notify_one() 最多影响一个 wait(),早于通知的等待不会被“积压”,晚于通知的等待会一直挂起,直到下次通知。这意味着,如果你的逻辑依赖“通知次数”,就得自己用计数器或队列配合 condition_variable。










