std::unique_lock支持延迟加锁,需显式传入std::defer_lock;std::lock_guard不支持该功能。延迟加锁用于条件变量wait、超时尝试及精细锁生命周期控制。

std::unique_lock 本身不支持“延迟加锁”,但可以配合 std::defer_lock 使用
直接调用 std::unique_lock 构造函数默认会立即尝试加锁,这不是延迟——所谓“延迟加锁”,实际是指构造时不加锁,后续按需手动调用 lock()。关键在于必须显式传入 std::defer_lock 标签。
- 错误写法:
std::unique_lock<:mutex> lk(mtx);</:mutex>→ 立即阻塞加锁 - 正确写法:
std::unique_lock<:mutex> lk(mtx, std::defer_lock);</:mutex>→ 构造时不加锁,lk.owns_lock()返回false - 后续可安全调用
lk.lock()(阻塞)或lk.try_lock()(非阻塞)
为什么不用 std::lock_guard?它根本不提供延迟选项
std::lock_guard 是 RAII 锁守卫,设计上只支持构造时加锁、析构时释放,没有 lock()/unlock() 成员函数,也不接受 std::defer_lock。想实现延迟控制,std::unique_lock 是唯一标准选择。
-
std::lock_guard更轻量,适用于“进作用域就锁、出作用域就放”的简单场景 -
std::unique_lock多了运行时所有权转移、条件变量配合、延迟/尝试加锁等能力,代价是轻微内存和性能开销(内部多一个布尔状态位) - 别为了“延迟”硬套
std::lock_guard—— 编译直接报错:没有匹配的构造函数
配合条件变量时,延迟加锁是必须的
使用 std::condition_variable::wait() 时,必须传入一个已持有锁的 std::unique_lock,且该锁会在 wait 内部自动释放,并在唤醒后重新获取。这个过程要求锁对象能被临时释放和重获,只有 std::unique_lock 支持。
- 典型模式:
std::unique_lock lk(mtx, std::defer_lock);→lk.lock();→cv.wait(lk, []{ return ready; }); - 不能把
std::defer_lock省略,否则wait()调用前锁已持有,但wait()要求锁处于“已锁定且可移交”状态 - 如果提前
lk.unlock()再调wait(),会触发std::system_error(错误码operation_not_permitted)
try_lock() + defer_lock 是实现超时等待的常见组合
当需要“最多等 100ms,拿不到锁就放弃”,不能只靠 std::defer_lock,得用 try_lock_for() 或 try_lock_until()。它们底层依赖 std::unique_lock 的可迁移性,且仅对 std::defer_lock 初始化的对象有意义。
立即学习“C++免费学习笔记(深入)”;
- 示例:
std::unique_lock lk(mtx, std::defer_lock); if (lk.try_lock_for(100ms)) { /* 拿到锁 */ } - 注意:
try_lock_for()是阻塞式尝试,不是轮询;它会挂起线程直到超时或成功 - 若用普通
std::mutex::try_lock(),无法与条件变量协同,也无法自动管理作用域释放 - Windows 上某些旧 libc++ 实现对
try_lock_for的时钟精度支持较弱,建议测试实际延时
真正容易被忽略的是:延迟加锁不是功能目标,而是为更精细的锁生命周期控制服务。比如在锁住前做参数校验、或根据条件决定是否加锁——这时候 std::defer_lock 初始化只是起点,后续的 lock() / try_lock() / unlock() 调用时机,才真正决定线程安全边界。


















