理解 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);

相关推荐
朔北之忘 Clancy3 小时前
2024 CSP-J 第二轮真题解析
c++·算法·青少年编程·noip·csp·信奥赛·noi
Lazionr3 小时前
set和map的使用——从基础接口到实际应用
c++
YYYing.3 小时前
【设计模式系列 (五) 】原型模式
开发语言·后端·设计模式·原型模式·c/c++
重生之小比特3 小时前
【C++进阶】哈希表实现
开发语言·c++·哈希
m0_380743873 小时前
VisualStudio中OpenCV的创建与配置使用小结
开发语言·c++
凉茶钱3 小时前
【C++】动态内存管理完整解析:从内存划分到 new delete 底层原理
c语言·开发语言·c++·内存管理·动态内存
cu1433 小时前
细谈GM8775C的具体功能和应用
c语言·c++·人工智能·嵌入式硬件
小七在进步3 小时前
类和对象(五)
java·开发语言·算法
无名猿3 小时前
std::optional 完全指南:别再用 -1 和 nullptr 表达「没有值」
c++·标准库·现代c++·语法基础
Rebecca_aLi3 小时前
码匠教育:零基础学 Python,标识符到循环全套基础语法解析
开发语言·python·正则表达式