unique_lock构造后必须已加锁才能wait,否则抛异常;wait需用while而非if以防虚假唤醒;notify应在同一锁内更新状态以保证可见性;adopt_lock仅用于已持锁时移交管理权。

为什么不能直接用 unique_lock 构造完就 wait?
因为 condition_variable::wait 要求传入的 unique_lock 必须处于「已加锁」状态,且该锁必须和条件变量关联的互斥量类型一致(通常是 std::mutex)。如果构造 unique_lock 时不加锁(比如用 std::defer_lock),或者锁在 wait 前被意外释放,wait 会抛出 std::system_error,错误信息通常是 operation not permitted 或 invalid argument。
常见错误写法:
std::mutex mtx;
std::condition_variable cv;
std::unique_lock<std::mutex> lk(mtx, std::defer_lock); // 没锁!
cv.wait(lk, []{ return ready; }); // 运行时报错
正确做法是:确保 lk 在调用 wait 前已持有锁——通常直接默认构造或传 std::try_to_lock 后检查。
wait 里为什么要用 while 而不是 if?
虚假唤醒(spurious wakeup)是 POSIX 和 C++ 标准允许的行为:wait 可能在没有 notify_one/notify_all 的情况下返回。如果只用 if 判断一次,线程可能在条件未满足时继续执行,导致逻辑错误。
立即学习“C++免费学习笔记(深入)”;
实操建议:
- 始终用
while循环检查谓词,而不是if - 谓词必须是无副作用、轻量的 lambda 或函数对象
- 避免在谓词里做耗时操作(比如 IO、锁其他资源)
示例:
bool ready = false;
// ...
std::unique_lock<std::mutex> lk(mtx);
while (!ready) {
cv.wait(lk); // 自动释放锁 → 等待 → 被唤醒后重新加锁
}
// 此时 ready 为 true,且 lk 仍持有 mtx
notify 一定要在锁住同一互斥量的情况下调用吗?
不是强制要求,但强烈建议这么做。原因有二:
一是避免竞态:如果 notify 在修改共享状态(如 ready = true)前没加锁,等待线程可能在 wait 返回后读到旧值;二是提升可预测性——notify 不会立即唤醒线程,只是“标记可唤醒”,真正调度由系统决定。若通知和状态更新不同步,容易漏唤醒。
典型安全模式:
std::unique_lock<std::mutex> lk(mtx); ready = true; cv.notify_one(); // 在锁内 notify,保证 ready 对等待线程可见
注意:notify_one 和 notify_all 本身不操作互斥量,也不要求锁已持有,但配合状态更新时,锁是必要的同步手段。
什么时候该用 std::adopt_lock?
当你已经持有了互斥量,想把所有权转移给 unique_lock(比如在函数参数传递或跨作用域管理时),才用 std::adopt_lock。它不会尝试加锁,只是告诉 unique_lock:“这把锁我已拿着,请接管管理权”。
误用场景很多,比如:
std::mutex mtx; mtx.lock(); std::unique_lock<std::mutex> lk(mtx, std::adopt_lock); // ✅ 正确:已锁,移交管理 // ... mtx.unlock(); // ❌ 错误:lk 析构时会自动 unlock,这里再 unlock 是 double-unlock UB
关键点:
-
adopt_lock前必须已通过mtx.lock()或等价方式成功加锁 - 之后不能再手动调用
mtx.unlock(),否则未定义行为 - 绝大多数简单场景用默认构造(自动加锁)更安全,
adopt_lock属于进阶控制,容易出错
条件变量配合的核心其实是「状态 + 锁 + 通知」三者顺序和可见性的对齐,细节松动一点,就可能卡死或读到脏数据。


















