当前位置: 首页 > article >正文

深入探讨C++中的互斥锁管理:`std::lock_guard`与`std::unique_lock`

在C++多线程编程的世界里,确保数据在并发访问时的一致性和安全性是至关重要的。互斥锁(mutex)是实现这一目标的关键工具之一。然而,手动管理互斥锁往往容易出错,尤其是在异常处理和复杂的控制流中。C++11标准库引入了两种基于RAII(Resource Acquisition Is Initialization)概念的互斥锁管理工具:std::lock_guardstd::unique_lock。这两种工具不仅简化了互斥锁的使用,还帮助开发者避免了因忘记解锁而导致的死锁问题。本文将深入探讨这两种锁管理工具的内部机制、使用场景、代码示例和最佳实践。

std::lock_guard:简单而有效的锁管理

std::lock_guard是一个简单而有效的互斥锁管理器,它在构造时自动获取互斥锁,并在析构时自动释放互斥锁。这种设计使得std::lock_guard非常适合简单的锁管理场景,其中锁的生命周期与对象的生命周期完全一致。

内部机制

std::lock_guard的内部机制非常简单。它继承自std::lock_guard_base,并在构造函数中调用互斥锁的lock()方法,在析构函数中调用互斥锁的unlock()方法。这种设计确保了即使在发生异常时,互斥锁也能被正确释放。

使用场景

std::lock_guard适用于那些在进入临界区时需要锁住,且在退出临界区时需要解锁的场景。由于其简单性,它通常用于保护较短的代码块,这些代码块的执行路径清晰,不涉及复杂的控制流。

代码示例

#include <mutex>
#include <iostream>

std::mutex mtx;

void print_message(const std::string& message) {
    std::lock_guard<std::mutex> lock(mtx); // 在这里自动上锁
    std::cout << message << std::endl;
    // 在这里自动解锁,即使发生异常也会解锁
}

std::unique_lock:灵活而强大的锁管理

std::lock_guard相比,std::unique_lock提供了更多的灵活性。它允许开发者在构造时选择是否立即锁定互斥量,并支持手动解锁和重新锁定。

内部机制

std::unique_lock的内部机制比std::lock_guard复杂。它提供了多个构造函数,允许开发者在构造时选择是否立即锁定互斥量。此外,它还提供了lock()unlock()try_lock()等方法,允许开发者在运行时控制互斥锁的状态。

使用场景

std::unique_lock适用于需要更灵活控制互斥锁的场景,例如:

  • 延迟锁定:在某些条件下才需要锁定互斥量。
  • 显式解锁/重新锁定:在某些条件下需要提前解锁,然后在后续的代码中重新锁定。
  • 与条件变量一起使用:在等待条件变量时需要解锁互斥量,当条件满足时重新锁定。

代码示例

#include <mutex>
#include <iostream>
#include <thread>
#include <condition_variable>

std::mutex mtx;
std::condition_variable cv;
bool ready = false;

void print_id(int id) {
    std::unique_lock<std::mutex> lock(mtx, std::defer_lock); // 延迟锁定
    cv.wait(lock, [&]{ return ready; }); // 等待条件变量
    std::cout << "ID: " << id << std::endl;
    // 锁在这里自动释放
}

void go() {
    {
        std::unique_lock<std::mutex> lock(mtx);
        ready = true;
        lock.unlock(); // 显式解锁,以便其他线程可以锁定
    }
    cv.notify_all();
}

int main() {
    std::thread threads[10];
    for (int i = 0; i < 10; ++i) {
        threads[i] = std::thread(print_id, i);
    }
    std::cout << "10 threads ready to race...\n";
    go(); // 通知所有线程开始执行
    for (auto& th : threads) {
        th.join();
    }
    return 0;
}

最佳实践

  • 使用std::lock_guard:当你需要保护一个简单的代码块,且不需要在代码块内部解锁时,使用std::lock_guard
  • 使用std::unique_lock:当你需要更灵活的锁管理,如延迟锁定、显式解锁/重新锁定,或者需要与条件变量一起使用时,使用std::unique_lock
  • 避免死锁:确保在可能抛出异常的代码块中正确管理互斥锁,以避免死锁。
  • 避免不必要的锁定:只在必要时锁定互斥量,以减少不必要的性能开销。

总结

std::lock_guardstd::unique_lock都是C++标准库中强大的工具,它们帮助开发者以更安全、更简洁的方式管理互斥锁。选择使用哪一个取决于具体的应用场景。希望本文能帮助你更好地理解和使用std::lock_guardstd::unique_lock,在多线程编程中写出更安全、更高效的代码。


http://www.kler.cn/a/458902.html

相关文章:

  • C语言格式输出
  • 算法排序算法
  • STM32-笔记18-呼吸灯
  • springboot中,怎么把对象的空属性转为json的时候过滤掉
  • Java 集合框架之 List、Set 和 Map 的比较与使用
  • overscroll-behavior-解决H5在ios上过度滚动的默认行为
  • C++ 设计模式:模板方法(Template Method)
  • Zookeeper中version-2目录下存放数据
  • 生态碳汇涡度相关监测与通量数据分析实践技术应用
  • mysql一个表只能由一个自动递增列吗
  • CKA认证 | Day7 K8s存储
  • 封装一个自己的JS或TS库,并发布到npm上
  • 网络基础入门到深入(2):网络协议-TCP/IP协议栈
  • PowerShell 常见问题解答
  • 深度学习工作:从追求 SoTA 到揭示新现象
  • 数据库的概念和操作
  • Mysql高级
  • 天通卫星技术,保障无信号区域的高效通信与监测
  • C++笔记4:C和C++混合编程,C不能识别C++的类内函数编译报错怎么办
  • 【优选算法】Sliding-Chakra:滑动窗口的算法流(下)
  • 【uni-app】微信小程序使用lime-painter生成海报
  • 区块链安全常见的攻击分析——私有数据泄露 (Private Data Exposure)【7】
  • Javascript数据结构——图Graph
  • C++ 设计模式:代理模式(Proxy Pattern)
  • 力扣第116题:填充每个节点的下一个右侧节点指针 - C语言解法
  • 代码随想录day21 | leetcode 77.组合 77.组合 加剪枝操作 216.组合总和III