std::condition_variable必须与std::mutex配合使用,单独wait会崩溃;须用while循环重检条件防虚假唤醒;notify_one/notify_all需依场景选择;超时等待需注意chrono单位与返回值判断。

std::condition_variable 必须和 std::mutex 一起用,单独 wait 会崩溃
它不是独立同步原语,本质是“条件等待队列”,不维护自身状态。没配 std::mutex 就调 wait(),轻则死锁,重则触发 std::system_error(错误信息通常是 operation not permitted 或 invalid argument)。
正确姿势:先锁住 std::mutex,再检查条件、调 wait();wait() 内部会自动解锁并挂起线程,被唤醒后重新加锁再返回。
- 永远用
wait(lock, predicate)形式,别用无谓的wait(lock)—— 容易漏掉虚假唤醒 -
predicate必须是可调用对象(lambda 最常用),且必须访问同一份受保护的共享变量 - 唤醒方(
notify_one()/notify_all())不一定需要持有锁,但持有更安全(避免 notify 早于 wait 的竞态)
虚假唤醒(spurious wakeup)不是 bug,是 POSIX 和 C++ 标准允许的行为
即使没人调 notify_*,wait() 也可能返回。这不是实现缺陷,而是底层 futex 或 pthread_cond_wait 的设计取舍。忽略它,程序大概率在高负载或特定内核版本下偶发逻辑错乱。
所以不能写 if (condition == false) cv.wait(lock);,必须用 while 循环重检:
立即学习“C++免费学习笔记(深入)”;
while (!data_ready) {
cv.wait(lock);
}
这个 while 不是性能浪费 —— 真实场景中虚假唤醒极罕见,但一次漏判就可能让线程跳过本该等待的条件。
notify_one() 和 notify_all() 的选择直接影响吞吐和公平性
用错会导致线程饥饿或资源争抢加剧。比如生产者-消费者模型里,多个消费者等待空缓冲区,只用 notify_one() 没问题;但如果多个线程等同一个完成信号(如 all_tasks_done),用 notify_one() 就会漏掉其余线程。
-
notify_one()开销小、唤醒精准,适合“一个条件满足 → 一个线程干活”的场景 -
notify_all()保证不漏,但所有等待线程都竞争锁,可能引发 thundering herd(惊群),尤其在线程数多时明显拖慢 - 没有“notify_if”这种东西 —— 判断逻辑必须放在 wait 的 predicate 里,而不是靠 notify 侧过滤
std::condition_variable 不支持超时精度控制,chrono 单位选错会误判
wait_for() 和 wait_until() 的超时参数类型是 std::chrono::duration,但很多人传 std::chrono::milliseconds(100) 却期望 100ms 绝对准时 —— 实际上系统调度粒度、锁竞争都会导致延迟。更隐蔽的问题是单位混淆:
// ❌ 错误:把纳秒当毫秒用,实际只等了 100 纳秒 cv.wait_for(lock, std::chrono::nanoseconds(100)); // ✅ 正确:明确单位,且接受“至少等待”语义 cv.wait_for(lock, std::chrono::milliseconds(100));
另外,wait_for() 返回值是 std::cv_status,不是布尔值 —— 必须显式判断是否超时,否则可能把超时当成条件满足。
真正难处理的是“等待带优先级的条件”或“跨进程同步”,std::condition_variable 做不到 —— 它只在单进程内有效,且不提供优先级队列。这时候得换 std::counting_semaphore(C++20)或平台原生机制。










