Skip to content

ipc::rw_lock

mutouyun edited this page Dec 9, 2025 · 2 revisions

读写锁(Reader-Writer Lock),支持多个读者并发访问,写者独占访问。基于自旋锁实现。

namespace ipc {

class rw_lock {
public:
    rw_lock() = default;
    
    // 禁止拷贝和移动
    rw_lock(const rw_lock&) = delete;
    rw_lock& operator=(const rw_lock&) = delete;
    rw_lock(rw_lock&&) = delete;
    rw_lock& operator=(rw_lock&&) = delete;

    void lock() noexcept;           // 写锁(独占)
    void unlock() noexcept;         // 释放写锁
    
    void lock_shared() noexcept;    // 读锁(共享)
    void unlock_shared() noexcept;  // 释放读锁
};

} // namespace ipc

概述

ipc::rw_lock是一个基于原子操作的读写锁实现,允许:

  • 多个读者同时持有读锁(共享访问)
  • 单个写者独占写锁(排他访问)
  • 写者优先:有写者等待时,新的读者会阻塞

特点

  • ✅ 多读者并发,提升读密集场景性能
  • ✅ 写者独占,保证数据一致性
  • ✅ 无系统调用,基于原子操作
  • ⚠️ 仅适用于进程内同步
  • ⚠️ 写者会阻塞所有读者

成员函数

成员函数 说明
lock 获取写锁(独占)
unlock 释放写锁
lock_shared 获取读锁(共享)
unlock_shared 释放读锁

lock

void lock() noexcept;

获取写锁(独占访问)。会阻塞直到:

  • 没有其他写者持有锁
  • 所有读者都释放了锁

返回值:无

异常:不抛出异常(noexcept)

unlock

void unlock() noexcept;

释放写锁。

返回值:无

异常:不抛出异常(noexcept)

lock_shared

void lock_shared() noexcept;

获取读锁(共享访问)。会阻塞直到:

  • 没有写者持有锁
  • 允许多个读者同时持有读锁

返回值:无

异常:不抛出异常(noexcept)

unlock_shared

void unlock_shared() noexcept;

释放读锁。

返回值:无

异常:不抛出异常(noexcept)


使用示例

基本用法

#include "libipc/rw_lock.h"
#include <thread>
#include <vector>

ipc::rw_lock lock;
int shared_data = 0;

// 写者
void writer() {
    lock.lock();
    shared_data++;
    std::cout << "Writer updated data to " << shared_data << std::endl;
    lock.unlock();
}

// 读者
void reader(int id) {
    lock.lock_shared();
    std::cout << "Reader " << id << " read data: " << shared_data << std::endl;
    lock.unlock_shared();
}

int main() {
    std::vector<std::thread> threads;
    
    // 启动多个读者(可以并发)
    for (int i = 0; i < 5; ++i) {
        threads.emplace_back(reader, i);
    }
    
    // 启动写者
    threads.emplace_back(writer);
    
    for (auto& t : threads) {
        t.join();
    }
    
    return 0;
}

RAII封装

// 写锁守卫
class write_lock_guard {
    ipc::rw_lock& lock_;
public:
    explicit write_lock_guard(ipc::rw_lock& lock) : lock_(lock) {
        lock_.lock();
    }
    ~write_lock_guard() {
        lock_.unlock();
    }
    write_lock_guard(const write_lock_guard&) = delete;
    write_lock_guard& operator=(const write_lock_guard&) = delete;
};

// 读锁守卫
class read_lock_guard {
    ipc::rw_lock& lock_;
public:
    explicit read_lock_guard(ipc::rw_lock& lock) : lock_(lock) {
        lock_.lock_shared();
    }
    ~read_lock_guard() {
        lock_.unlock_shared();
    }
    read_lock_guard(const read_lock_guard&) = delete;
    read_lock_guard& operator=(const read_lock_guard&) = delete;
};

// 使用RAII守卫
void safe_write() {
    write_lock_guard guard(lock);
    shared_data++;  // 自动加锁和解锁
}

void safe_read() {
    read_lock_guard guard(lock);
    int value = shared_data;  // 自动加锁和解锁
}

读多写少场景

// 缓存系统示例
class Cache {
    ipc::rw_lock lock_;
    std::map<std::string, std::string> data_;

public:
    // 读操作(高频,使用共享锁)
    std::string get(const std::string& key) {
        lock_.lock_shared();
        auto it = data_.find(key);
        std::string result = (it != data_.end()) ? it->second : "";
        lock_.unlock_shared();
        return result;
    }
    
    // 写操作(低频,使用独占锁)
    void set(const std::string& key, const std::string& value) {
        lock_.lock();
        data_[key] = value;
        lock_.unlock();
    }
};

性能特性

适用场景

适合使用rw_lock的场景

  • 读操作频繁,写操作较少
  • 读操作耗时较短
  • 需要多个读者并发访问
  • 读者之间无需互斥

不适合使用rw_lock的场景

  • 写操作频繁(应使用spin_lock或mutex)
  • 读操作很少(额外开销不值得)
  • 需要进程间同步(应使用ipc::sync::mutex)
  • 读操作耗时很长(可能饥饿写者)

性能对比

在读多写少场景下的性能(10读者:1写者):

锁类型 吞吐量
rw_lock 高 (读者并发)
spin_lock 中 (完全互斥)
mutex 中 (完全互斥)

实现细节

内部实现

class rw_lock {
    using lc_ui_t = std::uint32_t;
    std::atomic<lc_ui_t> lc_ { 0 };
    
    enum : lc_ui_t {
        w_mask = 0x7FFF'FFFF,  // 读者计数掩码
        w_flag = 0x8000'0000   // 写者标志位
    };
public:
    void lock() noexcept {
        // 1. 设置写者标志
        // 2. 等待所有读者退出
    }
    
    void lock_shared() noexcept {
        // 1. 等待写者标志清除
        // 2. 增加读者计数
    }
};

关键点

  • 使用32位原子整数:最高位是写者标志,低31位是读者计数
  • 写者设置标志位后等待读者计数降为0
  • 读者检查写者标志,无写者时增加计数
  • 使用CAS (Compare-And-Swap) 保证原子性

注意事项

  • 写者优先:有写者等待时,新的读者会被阻塞
  • 不可递归:同一线程不能递归加锁
  • 不可升级:不能将读锁升级为写锁(会死锁)
  • 不可降级:不能将写锁降级为读锁(不支持)
  • 饥饿可能:写者过多可能导致读者饥饿
  • 进程内使用:仅用于同一进程内的线程同步
  • 避免长时间持锁:读者持锁时间过长会阻塞写者

相关文档

  • ipc::spin_lock - 自旋锁
  • ipc::sync::mutex - 进程间互斥锁
  • rw_lock.h - 头文件文档

Clone this wiki locally