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是更好的选择。