std::condition_variable必须与std::unique_lock配合使用,因wait()需临时解锁并重锁;须用while循环或带谓词的wait()防虚假唤醒;notify_one()适用于单任务唤醒,notify_all()用于多线程等待同一状态;析构前须确保无线程在wait()中。

std::condition_variable 必须和 std::unique_lock<:mutex> 一起用
单独声明 std::condition_variable 没用,它不管理锁,只负责挂起/唤醒线程。你得配一个 std::mutex 和对应的 std::unique_lock —— 用 std::lock_guard 会编译失败,因为 wait() 需要能临时释放并重新获取锁的能力。
-
wait()内部会先解锁传入的std::unique_lock,等被唤醒后再尝试重新加锁;std::lock_guard不支持手动解锁,直接报错 - 别在
wait()前自己unlock(),会导致竞态:解锁后、wait 前可能被 signal 打断,信号丢失 - 推荐写法:
std::unique_lock<:mutex> lk(mtx); cv.wait(lk, []{ return ready; });</:mutex>
虚假唤醒(spurious wakeup)必须用 while 循环检查条件
即使没人调 notify_one() 或 notify_all(),wait() 也可能返回。这是 POSIX 和 C++ 标准允许的行为,不是 bug。所以不能用 if 判断条件,否则可能跳过实际检查。
- 错误写法:
if (data_ready) { process(); }→ 可能 data_ready 仍是 false - 正确写法:
while (!data_ready) { cv.wait(lk); }或用带谓词的重载:cv.wait(lk, [&]{ return data_ready; }); - 谓词版本本质就是帮你写了 while 循环,更安全,也避免手抖漏掉
notify_one() 和 notify_all() 的选择影响性能和语义
两者都只唤醒「当前等待中的」线程,不会影响之后才调 wait() 的线程。区别在于唤醒数量,选错可能造成饥饿或无谓开销。
-
notify_one():适合“一个任务对应一个消费者”的场景(比如生产者放一个 job,一个 worker 处理),唤醒一个就够了 -
notify_all():适合“多个线程等同一个状态变化”(比如所有 worker 等待 shutdown 信号),但注意:所有被唤醒线程会争抢 mutex,只有第一个能进,其余继续阻塞 —— 这是常见性能坑点 - 别在持有锁时长时间做耗时操作后才 notify,会拖慢唤醒响应;notify 本身很快,尽量靠近状态更新后立刻调
析构前必须确保没线程还在 wait()
std::condition_variable 对象销毁时,如果还有线程卡在 wait() 中,行为未定义 —— 多数实现会 crash 或 hang。这不是资源泄漏,是使用逻辑错误。
立即学习“C++免费学习笔记(深入)”;
- 常见出错位置:类成员变量是
std::condition_variable,但析构函数没等所有等待线程退出就直接 return - 解决方法:配合
std::atomic<bool></bool>或标志位 +join(),确保所有 worker 线程已退出或收到终止通知再析构 cv - 别依赖“线程自己检查 flag 就会退出”,必须显式同步:发通知 → 等 join → 析构 cv










