040-c++递归互斥锁、lock_guard和unique_lock
std::recursive_mutex
可以在同一个线程中嵌套使用。介绍了 lock_guard 和 mutex 是什么关系, lock_guard 和 unique_lock 的区别
std::recursive_mutex
是一种递归锁,它允许同一线程多次加锁而不会导致死锁。在使用普通的std::mutex
时,同一线程如果多次尝试加锁会造成死锁;而std::recursive_mutex
则不会。它会跟踪当前线程的加锁次数,并在解锁时,只有当解锁次数与加锁次数相同时,锁才会真正释放。
以下是一个简单的示例,展示了如何在同一线程中嵌套使用std::recursive_mutex
:
1 |
|
在这个例子中,recursiveFunction
函数递归调用自己,每次调用时都会加锁,直到count
为零时停止递归。由于使用了std::recursive_mutex
,即使在同一线程中多次加锁,也不会发生死锁。
lock_guard 和 mutex 是什么关系
std::lock_guard
和std::mutex
在 C++中是协同使用的关系。std::lock_guard
是一个用于管理std::mutex
(或其他锁类型)的 RAII(资源获取即初始化)机制,旨在更安全和便捷地管理互斥锁的加锁和解锁过程。
它们的关系
- **
std::mutex
**:是 C++标准库中提供的基础互斥锁类,用于在线程间保护共享资源,避免数据竞争。通过显式调用lock()
和unlock()
函数加锁和解锁来控制对共享资源的访问。 - **
std::lock_guard
**:是一个模板类,用于自动管理std::mutex
的加锁和解锁操作。它在构造时自动对传入的std::mutex
对象加锁,在销毁时(通常是作用域结束时)自动解锁。因此,使用std::lock_guard
可以确保即使在异常或函数提前返回时,锁也会被正确释放。
为什么要使用std::lock_guard
如果手动控制std::mutex
的加锁和解锁,很容易因漏掉解锁操作或在出现异常时未释放锁而导致死锁。std::lock_guard
简化了这一过程,通过在其生命周期内自动加锁和解锁,确保了代码的安全性和可读性。
示例代码
1 |
|
在上面的例子中,std::lock_guard
构造时会锁住mtx
,在safeFunction
结束时,std::lock_guard
自动析构并解锁,从而避免了手动解锁可能带来的问题。
总结
std::lock_guard
和std::mutex
是合作关系:std::mutex
是互斥锁的实现,而std::lock_guard
是用于自动管理std::mutex
加锁和解锁的工具。
lock_guard 和 unique_lock 的区别
std::lock_guard
和std::unique_lock
都是用于管理std::mutex
的 RAII 类型锁,但它们在功能和灵活性上有一些区别。下面详细解释它们的差异。
1. 锁的灵活性
- **
std::lock_guard
**:std::lock_guard
非常简单,在构造时自动加锁,并且在析构时自动解锁。它没有提供其他任何锁管理的接口,因此无法手动解锁后再重新加锁。 - **
std::unique_lock
**:std::unique_lock
更灵活,它提供了加锁、解锁和重锁的功能。你可以在代码中选择性地调用lock()
和unlock()
来手动控制锁的状态。
2. 延迟加锁和解锁
- **
std::lock_guard
**:在创建std::lock_guard
对象时必须立即加锁,没有延迟加锁的选项。 - **
std::unique_lock
**:支持延迟加锁,即可以在构造时不加锁,稍后在需要的时候调用lock()
来加锁。可以使用std::defer_lock
标志来延迟加锁。
3. 条件变量的支持
- **
std::lock_guard
**:无法与std::condition_variable
配合使用,因为条件变量要求能够临时解锁和重新加锁,这需要更灵活的锁管理。 - **
std::unique_lock
**:可以与std::condition_variable
配合使用。在等待条件变量时,std::unique_lock
可以暂时解锁互斥锁,以便其他线程获得锁,并在条件满足后重新加锁。
4. 开销
- **
std::lock_guard
**:轻量级,没有额外的状态信息,因为它一旦加锁,就保持锁定状态直到销毁。适用于简单的场景。 - **
std::unique_lock
**:稍微重一些,因为它维护了更多的状态信息(比如是否锁定、是否延迟等),以便支持更多功能。适用于需要灵活控制锁的场景。
使用示例
std::lock_guard
1 |
|
std::unique_lock
1 |
|
总结
特性 | std::lock_guard |
std::unique_lock |
---|---|---|
加锁和解锁灵活性 | 固定,只能自动加锁和解锁 | 灵活,支持手动加锁、解锁、重新加锁 |
延迟加锁 | 不支持 | 支持,通过std::defer_lock 指定 |
条件变量支持 | 不支持 | 支持 |
适用场景 | 简单加锁、解锁 | 需要更多锁管理控制的复杂场景 |
性能 | 更轻量级 | 较重,带有额外状态信息 |
总体而言,如果只需要简单的加锁解锁,std::lock_guard
更合适;如果需要更灵活的锁控制(如条件变量、延迟加锁等),则std::unique_lock
是更好的选择。