condition_variable 必须与 std::mutex 配合使用,且 wait() 必须传入 std::unique_lock 而非 std::mutex 或 std::lock_guard,因其需原子地释放锁并挂起线程;notify_one() 适用于单消费者场景,notify_all() 用于多线程等待同一条件;务必用 while 循环检查谓词以防虚假唤醒;其生命周期必须长于所有调用 wait() 的线程,否则导致未定义行为。

condition_variable 本身不能单独用,必须和 std::mutex 配合,且等待时必须用 std::unique_lock<:mutex> —— 这是绝大多数人踩坑的起点。
为什么 wait() 要传 unique_lock 而不是 mutex?
因为 wait() 内部会先原子地释放锁、挂起线程,等被唤醒后再重新加锁;这个“释放+挂起”的动作必须是原子的,std::mutex 不支持临时交出所有权,而 std::unique_lock 可以移动、可释放、可重入。
常见错误写法:
std::mutex mtx; std::condition_variable cv; // ❌ 错误:不能传 std::lock_guard 或裸 mutex cv.wait(std::lock_guard(mtx), []{ return ready; }); // ✅ 正确:必须用 unique_lock std::unique_lock lock(mtx); cv.wait(lock, []{ return ready; });
notify_one() 和 notify_all() 到底该选哪个?
取决于你预期有几个线程在等。如果逻辑上只允许一个消费者处理任务(比如生产者-消费者中单个任务只被取一次),用 notify_one() 更高效;如果多个线程都在等同一个条件(比如所有工作线程等“全部数据加载完成”),必须用 notify_all()。
立即学习“C++免费学习笔记(深入)”;
容易忽略的点:
-
notify_one()不保证唤醒的是“最久没运行”的线程,调度由 OS 决定 - 即使只调
notify_one(),也要用 while 循环检查条件,防止虚假唤醒(spurious wakeup) -
notify_*()可以在锁外调用,但若在锁内调用,能避免唤醒后立即竞争锁(微优化,非必须)
wait() 的谓词版本为什么必须用 while 而不是 if?
因为 c++ 标准允许线程在没被显式唤醒的情况下返回(虚假唤醒),也因为多个线程可能同时被唤醒,但只有第一个能真正满足条件——后续线程醒来时条件可能已失效。
正确模式:
std::unique_lock lock(mtx); while (!ready) { // ❌ 不能用 if cv.wait(lock); } // 此时 ready 一定为 true
如果换成 if,一旦发生虚假唤醒或条件被其他线程抢走,就会跳过检查直接执行后续逻辑,导致未定义行为。
condition_variable 的生命周期比线程长吗?
必须更长。如果 std::condition_variable 对象在某个线程还处于 wait() 状态时就被析构,程序直接 UB(通常 crash)。常见于类成员变量被提前销毁,或局部 condition_variable 被函数返回后销毁。
安全做法:
- 把
std::condition_variable作为类的成员,并确保它比所有使用它的线程活得久 - 在线程 join() 完成后再销毁 condition_variable
- 避免在 Lambda 捕获中隐式延长其生命周期(比如通过 shared_ptr 管理,但需小心循环引用)
最难调试的往往不是语法错,而是唤醒时机和锁持有范围不匹配——比如在修改共享状态前没加锁,或者 notify 放在锁外却假设接收方一定能立刻看到新值。