用互斥锁实现读写锁

多线程
Author

0warning0error

Published

July 12, 2024

字节有一道面试题:

使用普通的互斥锁实现读写锁

要向用mutex锁设计,首先要知道什么是读写锁。 读写锁的定义如下:

a readers–writer is a synchronization primitive that solves one of the readers–writers problems. An RW lock allows concurrent access for read-only operations, whereas write operations require exclusive access.

这里给出C++11版本的读写锁实现,参考了这个网站

class RWMutex
{
    std::mutex    mut_;
    std::condition_variable gate1_;
    std::condition_variable gate2_;
    unsigned state_;

    static const unsigned write_entered_ = 1U << (sizeof(unsigned)*CHAR_BIT - 1);
    static const unsigned n_readers_ = ~write_entered_;

public:

    RWMutex() : state_(0){}
    ~RWMutex(){}

    RWMutex(const RWMutex&) = delete;
    RWMutex& operator=(const RWMutex &) = delete;

    void lock() {
        //std::this_thread::disable_interruption _; std::thread 不可打断,但是std::jthread 可能会被打断
        std::unique_lock<std::mutex> lk(mut_);
        gate1_.wait(lk, [&](){return !is_write_entered();} ); // 要先保证没有写者进入
        state_ |= write_entered_;
        gate2_.wait(lk,[&](){return get_readers_num() == 0;}); // 要保证没有读者进入
    }

    bool try_lock() {
        std::unique_lock<std::mutex> lk(mut_, std::try_to_lock);
        if (lk.owns_lock() && state_ == 0)
        {
            state_ = write_entered_;
            return true;
        }
        return false;
    }
    void unlock()
    {
        {
            std::lock_guard<std::mutex> _(mut_);
            state_ = 0;
        }
        gate1_.notify_all();
    }

// Shared ownership
    void lock_shared() {
        //std::this_thread::disable_interruption _;
        std::unique_lock<std::mutex> lk(mut_);
        gate1_.wait(lk,[]{ return !is_write_entered() && get_readers_num() < n_readers_; });
        unsigned num_readers = get_readers_num() + 1;
        set_readers_num(num_readers);
    }
    inline unsigned get_readers_num(){
        return state_ & n_readers_;
    }

    inline bool is_write_entered(){
        return state_ & write_entered_;
    }

    inline void set_readers_num(unsigned num_readers){
        state_ &= ~n_readers_;
        state_ |= num_readers;
    }
    bool try_lock_shared() {
        std::unique_lock<std::mutex> lk(mut_, std::try_to_lock);
        unsigned num_readers = get_readers_num();
        if (lk.owns_lock() && !is_write_entered() && num_readers != n_readers_)
        {
            ++num_readers;
            set_readers_num(num_readers);
            return true;
        }
        return false;
    }
    void unlock_shared() {
        std::lock_guard<std::mutex> _(mut_);
        unsigned num_readers = get_readers_num() - 1;
        set_readers_num(num_readers);
        if (is_write_entered())
        {
            if (num_readers == 0)
                gate2_.notify_one(); // 唤醒等待的写者
        }
        else
        {
            if (num_readers == n_readers_ - 1)
                gate1_.notify_one(); // 如果读成员满了之后开始释放,就唤醒等待的读者
        }
    }
};