std::unique_lock<std::mutex>是 C++11 提供的一种更灵活的互斥锁管理器,常用于多线程同步场景。
std::unique_lock<std::mutex> uLock;
本质上是一个可控制加锁/解锁时机、可转移所有权的锁对象。
一、unique_lock是干什么的?
一句话总结:
unique_lock是 RAII 风格的互斥锁管理器,比lock_guard更灵活
| 特性 | lock_guard | unique_lock |
|---|---|---|
| 构造时自动加锁 | ✅ | ✅(可选) |
| 析构时自动解锁 | ✅ | ✅ |
| 手动 lock / unlock | ❌ | ✅ |
| 延迟加锁 | ❌ | ✅ |
| 可转移所有权 | ❌ | ✅ |
| 配合 condition_variable | ❌ | ✅(必须) |
二、最常见的用法(自动加锁)
std::mutex mtx;
void func()
{
std::unique_lock<std::mutex> uLock(mtx);
// 🔒 此处已加锁
// 临界区
do_something();
// 函数结束自动解锁
}
等价于:
mtx.lock();
do_something();
mtx.unlock();
但异常安全(中途 return / throw 都会自动 unlock)。
三、延迟加锁(嵌入式常用)
1️⃣std::defer_lock
std::unique_lock<std::mutex> uLock(mtx, std::defer_lock);
// 此时还没加锁
do_prepare();
// 手动加锁
uLock.lock();
// 临界区
do_critical();
// 可手动提前解锁
uLock.unlock();
👉非常适合那种:
先判断条件
再决定是否进入临界区的逻辑
四、手动 unlock / 再 lock(lock_guard做不到)
std::unique_lock<std::mutex> uLock(mtx); do_part1(); // 暂时释放锁 uLock.unlock(); // 执行耗时操作 sleep(1); // 再次加锁 uLock.lock(); do_part2();
五、和condition_variable搭配(必须用unique_lock)
标准生产者 / 消费者模型
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void consumer()
{
std::unique_lock<std::mutex> uLock(mtx);
cv.wait(uLock, [] { return ready; });
// wait 内部会:
// 1. unlock(mtx)
// 2. 阻塞等待
// 3. 被唤醒后 lock(mtx)
use_data();
}
void producer()
{
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
cv.notify_one();
}
⚠️重点:
cv.wait()只能接受unique_lock因为它需要在内部反复 unlock / lock
六、尝试加锁(try_lock)
std::unique_lock<std::mutex> uLock(mtx, std::try_to_lock);
if (uLock.owns_lock())
{
// 成功拿到锁
}
else
{
// 没拿到锁,直接返回
}
👉适合避免线程阻塞的场景(例如:状态上报、非关键日志)。
七、转移锁的所有权(高级用法)
std::unique_lock<std::mutex> lock1(mtx);
// 转移所有权
std::unique_lock<std::mutex> lock2 = std::move(lock1);
if (!lock1.owns_lock())
{
// lock1 已失效
}
📌 这个在:
线程池
任务队列
跨函数返回锁
场景中很有用。
八、与lock_guard的选择建议(记住这张表)
| 场景 | 用哪个 |
|---|---|
| 简单临界区 | lock_guard |
| 需要 wait / notify | unique_lock |
| 需要手动 unlock | unique_lock |
| 延迟加锁 | unique_lock |
| try_lock | unique_lock |
👉经验法则:
能用
lock_guard就别用unique_lock,但一旦复杂,就直接unique_lock
九、典型用法(示例)
有线程 + sleep / yield / 任务队列,典型写法应该是:
std::mutex mtx;
void worker()
{
while (running)
{
std::unique_lock<std::mutex> uLock(mtx);
if (task_queue.empty())
{
uLock.unlock();
std::this_thread::yield();
continue;
}
auto task = task_queue.front();
task_queue.pop();
uLock.unlock(); // 🔥 关键:避免锁内执行耗时任务
process(task);
}
}