理解 lock_guard, unique_lock 与 shared_lock 的设计哲学与应用场景

一. lock_guard

lock_guard 比较简单,利用RAII 方式,构造时加锁,析构时解锁

cpp 复制代码
template <class Mutex>
class lock_guard {
    Mutex& mutex_;
public:
    // 构造时加锁
    explicit lock_guard(Mutex& m) : mutex_(m) { mutex_.lock(); }

    // 支持adopt_lock:接管已经加过锁的mutex
    lock_guard(Mutex& m, std::adopt_lock_t) : mutex_(m) { }

    // 析构时解锁
    ~lock_guard() { mutex_.unlock(); }

    // 禁止拷贝(移动操作因存在用户声明的拷贝操作而不会被隐式生成)
    lock_guard(const lock_guard&) = delete;
    lock_guard& operator=(const lock_guard&) = delete;
};

这里的adopt_lock_t,比较简单,顾名思义领养一个孩子,不是你生的(加锁),但领养后需要照顾(解锁)

cpp 复制代码
std::mutex mtx;
int shared_data = 0;

void test() {
    std::lock_guard<std::mutex> lock(mtx);  
    ++shared_data;
}

二.unique_lock

cpp 复制代码
template <class Mutex>
class unique_lock {
    Mutex* mutex_;
    bool owns_;     // 是否持有锁
public:
    // 默认构造:不关联任何mutex
    unique_lock() noexcept : mutex_(nullptr), owns_(false) {}

    // 普通构造:立即加锁(注意owns_先设为false,lock成功后才设true,保证异常安全)
    explicit unique_lock(Mutex& m) : mutex_(&m), owns_(false) {
        mutex_->lock();
        owns_ = true;
    }

    // 延迟加锁:不立即加锁
    unique_lock(Mutex& m, std::defer_lock_t) : mutex_(&m), owns_(false) {}

    // 尝试加锁:非阻塞
    unique_lock(Mutex& m, std::try_to_lock_t)
        : mutex_(&m), owns_(m.try_lock()) {}

    // 接管已加锁的mutex
    unique_lock(Mutex& m, std::adopt_lock_t) : mutex_(&m), owns_(true) {}

    // 支持移动
    unique_lock(unique_lock&& other) noexcept;
    unique_lock& operator=(unique_lock&& other) noexcept;

    // 手动控制
    void lock();
    void unlock();
    bool try_lock();

    // 析构时:如果持有锁,解锁
    ~unique_lock() { if (owns_) mutex_->unlock(); }
};

跟lock_guard比,unique_lock最核心的区别就三个。

**一是能手动unlock/lock。**你可以在锁的生命周期中间释放锁、做一些不需要锁保护的操作、然后再重新加锁。这对控制锁的粒度至关重要,尤其是在锁保护区域内有耗时操作但这个操作本身不需要锁保护的场景下。

二是支持延迟加锁和try_lock。 defer_lock让你先构造unique_lock对象但不立即加锁,等后面你自己决定什么时候调lock()try_to_lock尝试加锁但不阻塞,加不上就返回false让你自己处理。这两个能力lock_guard完全没有。

**三是支持移动语义。**unique_lock可以从一个作用域移动到另一个作用域,锁的所有权跟着走。lock_guard既不能拷贝也不能移动,锁死在创建它的那个作用域。

三.shared_lock

cpp 复制代码
std::shared_mutex rw_mtx;
std::map<std::string, int> cache;

// 读操作:加共享锁,多个读线程可以并发
int read_cache(const std::string& key) {
    std::shared_lock lock(rw_mtx);  // 共享锁(读锁)
    auto it = cache.find(key);
    return it != cache.end() ? it->second : -1;
}

// 写操作:加独占锁,写的时候不允许任何读或写
void write_cache(const std::string& key, int value) {
    std::unique_lock lock(rw_mtx);  // 独占锁(写锁)
    cache[key] = value;
}

与unique_lock的区别

最大的区别是,对于的管理的对象,不会进行独占,其他的线程通过shared_lock也可以共同的管理共享互斥量(shared_mutex);

相关推荐
罗西的思考几秒前
【Agentic RL / 强化学习 / OPD】OpenClaw-RL 源码阅读笔记 --- (4)--- 系统架构
人工智能·算法·机器学习
QiLinkOS2 分钟前
从技术到资产的跃迁:企业专利布局的深层逻辑
c语言·数据结构·c++·单片机·嵌入式硬件·算法·开源
一只鹿鹿鹿4 分钟前
信息化项目管理规范(参考Word文件)
java·大数据·运维·开发语言·数据库
XGeFei8 分钟前
python中子线程与主线程的关系
开发语言·python
aini_lovee10 分钟前
FMCW雷达测速测距系统(锯齿波 + CFAR检测)
算法
Chase_______11 分钟前
【Java杂项】final 关键字详解:变量、方法、类限制与引用可变性
java·开发语言·python
qq_2975746713 分钟前
设计模式系列文章(基础篇第 11 篇):模板方法模式——定义算法骨架,实现代码复用与流程统一
算法·设计模式·模板方法模式
lqqjuly20 分钟前
知识蒸馏:理论、算法与可运行实现
人工智能·深度学习·算法
ruxingli21 分钟前
Golang iota详解
开发语言·后端·golang
我材不敲代码22 分钟前
Python venv 虚拟环境从入门到精通 + uv 高性能替代工具实战指南
开发语言·python·uv