std::unique_lock 是 C++ 标准库中的一个类,用于管理对互斥锁(std::mutex 或其派生类)的锁定和解锁。它提供了比 std::lock_guard 更灵活的控制,允许在需要时延迟锁定、提前解锁以及递归锁定等。
主要特性
- 延迟锁定:std::unique_lock 可以在创建时不立即锁定互斥锁,而是在稍后的时间点调用 lock() 或 try_lock() 方法来锁定。
- 提前解锁:通过调用 unlock() 方法,可以在不销毁 std::unique_lock 对象的情况下解锁互斥锁。这允许在锁定期间执行不需要互斥锁的操作。
- 所有权转移:std::unique_lock 对象可以通过移动语义(使用 std::move)来转移其所有权,从而在不释放锁的情况下将锁传递给另一个 std::unique_lock 对象。
- 递归锁定:如果互斥锁是可递归的(即 std::recursive_mutex),则 std::unique_lock 可以多次锁定同一个互斥锁,而不会产生死锁。
- 条件变量:std::unique_lock 通常与 std::condition_variable 一起使用,因为 std::condition_variable 的 wait() 方法需要一个 std::unique_lock 参数来自动解锁和重新锁定互斥锁。
使用方法
- 创建和锁定:
std::mutex mtx; | |
std::unique_lock<std::mutex> lck(mtx); // 立即锁定 mtx |
或者延迟锁定:
std::unique_lock<std::mutex> lck(mtx, std::defer_lock); // 不立即锁定 | |
lck.lock(); // 稍后锁定 |
- 提前解锁:
lck.unlock(); // 解锁 mtx |
注意,在解锁后重新使用 lck 前,必须确保没有其他线程已经锁定了 mtx,否则可能会导致未定义的行为。
- 所有权转移:
std::unique_lock<std::mutex> lck1(mtx); | |
std::unique_lock<std::mutex> lck2 = std::move(lck1); // lck1 不再拥有锁,lck2 现在拥有锁 |
- 与条件变量一起使用:
std::condition_variable cv; | |
std::mutex mtx; | |
std::unique_lock<std::mutex> lck(mtx); | |
cv.wait(lck, []{ /* 条件检查 */ }); // 在等待期间,lck 会自动解锁和重新锁定 mtx |
- 递归锁定(如果使用的是 std::recursive_mutex):
std::recursive_mutex rmtx; | |
std::unique_lock<std::recursive_mutex> rlck(rmtx); | |
rlck.lock(); // 可以多次锁定同一个 rmtx,不会产生死锁 |
注意事项
- 不要混合使用 std::lock_guard 和 std::unique_lock 来管理同一个互斥锁,因为这可能会导致未定义的行为。
- 在使用 std::unique_lock 时,要特别小心避免在解锁后重新使用它之前让其他线程锁定相同的互斥锁。
- std::unique_lock 在其析构函数中会自动解锁互斥锁(除非它已经处于未锁定状态)。因此,通常不需要显式调用 unlock(),除非有特定的需求。