std::condition_variable必须与std::unique_lock配对使用,因wait需临时释放并重获锁;须用while循环检测条件防虚假唤醒;notify应在状态更新后调用且避免锁内执行;销毁前须确保无线程在wait。

condition_variable 必须和 unique_lock 一起用
单独声明 std::condition_variable 没有意义,它不管理互斥状态,只负责挂起/唤醒线程。你必须配对使用 std::unique_lock<:mutex></:mutex> ——不是 std::lock_guard,也不是 std::shared_lock,更不能裸传 std::mutex。
常见错误是写成:
std::mutex mtx;
std::condition_variable cv;
// 错误:lock_guard 作用域一结束就 unlock,cv.wait 会直接抛 std::system_error
std::lock_guard<std::mutex> lk(mtx);
cv.wait(lk, []{ return ready; });
正确写法是让锁的生命周期覆盖整个 wait 调用:
std::unique_lock<std::mutex> lk(mtx);
cv.wait(lk, []{ return ready; }); // lk 在 wait 返回前始终持有(或被临时释放)
-
wait内部会自动unlock()当前线程的锁,进入等待;被唤醒后重新lock() - 用
unique_lock是因为它支持手动unlock()和转移所有权,lock_guard不支持 - 别在
wait的谓词里做耗时操作,否则阻塞锁时间过长,影响其他线程
虚假唤醒(spurious wakeup)必须用 while 循环检查条件
cv.wait 可能在没有 notify 的情况下返回,这是 POSIX 和 C++ 标准允许的行为。直接用 if 判断会导致逻辑错乱,比如生产者还没写数据,消费者就继续往下读了。
立即学习“C++免费学习笔记(深入)”;
正确模式永远是:
std::unique_lock<std::mutex> lk(mtx);
while (!ready) { // 注意:是 while,不是 if
cv.wait(lk);
}
// 此时 ready 为 true,且 lk 已重新加锁
- 即使没被 notify,
wait也可能返回,所以必须循环重检条件变量所依赖的状态 - 谓词形式
cv.wait(lk, []{ return ready; })内部也是 while 循环实现,安全但隐藏了细节 - 如果你在 wait 里修改了共享状态(比如 pop 队列),记得把修改逻辑也放进循环体,避免多线程竞争导致状态不一致
notify_one 和 notify_all 的选择影响性能和正确性
两者都只是“发信号”,不保证线程立即执行,也不保证唤醒顺序。区别在于:
-
notify_one唤醒一个在 wait 的线程(通常是等待最久的那个),适合“一对一”场景,比如单生产者-单消费者模型 -
notify_all唤醒所有 wait 线程,适合“一对多”或条件存在竞争的情况,比如多个线程等同一个资源就绪,但只有一个能抢到
典型陷阱:
// 错误:用 notify_one,但有多个消费者在等同一份数据 // 生产者: data = 42; ready = true; cv.notify_one(); // 只唤醒一个,其余永远卡住 // 正确:如果多个线程可能同时依赖 ready == true,用 notify_all cv.notify_all();
- 不要在持有锁期间调用 notify(虽然语法允许),它不需锁保护,提前 unlock 可减少锁争用
- notify 调用位置很重要:必须在修改完共享状态(如
ready = true)之后、且该状态对等待线程可见之后再调用 - notify 不会“保存”,如果调用时没有线程在 wait,信号就丢了 —— 所以状态变更和 notify 必须配套,不能靠 notify “触发”状态
condition_variable 不能跨线程销毁
如果还有线程在 wait,此时析构 std::condition_variable 会导致未定义行为(通常 crash 或 hang)。C++11 标准明确要求:销毁前确保没有线程处于 wait 状态。
安全做法是配合标志位 + join:
bool done = false;
std::mutex mtx;
std::condition_variable cv;
// 消费者线程
auto consumer = [&]() {
std::unique_lock<std::mutex> lk(mtx);
while (!done) {
cv.wait(lk, [&]{ return ready || done; });
if (done) break;
// 处理数据...
ready = false;
}
};
// 主线程准备退出
{
std::unique_lock<std::mutex> lk(mtx);
done = true;
cv.notify_all(); // 唤醒所有等待线程,让它们检查 done
}
consumer_thread.join(); // 等它退出后再析构 cv 和 mtx
- 永远不要在析构 mutex 或 condition_variable 前忽略仍在运行的 wait 线程
- 使用
done这类退出标志 +notify_all是最稳妥的协作终止方式 - 别依赖
std::thread::detach(),分离线程后无法 join,也就无法确认 wait 是否已退出











