读写锁的实现

Posted dirge

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了读写锁的实现相关的知识,希望对你有一定的参考价值。

reference:
原子操作、内存屏障、锁https://www.jianshu.com/p/1d90fe6627ad

哪些操作本身是原子的?

  • 单核处理器下中断发生在指令之间,因此单指令操作都是原子的
  • 多核处理器下进行零次或一次对齐内存访问的汇编指令是原子的

c++版读写锁 https://blog.csdn.net/zxc024000/article/details/88814461
volatile
谈谈C/C++中的volatile:https://zhuanlan.zhihu.com/p/33074506
内存模型:https://cloud.tencent.com/developer/article/1479199

  • 易变性。所谓的易变性,在汇编层面反映出来,就是两条语句,下一条语句不会直接使用上一条语句对应的volatile变量的寄存器内容,而是重新从内存中读取。
  • 不可优化性。volatile告诉编译器,不要对变量进行各种激进的优化,甚至将变量直接消除,保证程序员写在代码中的指令,一定会被执行。
  • 顺序性。能够保证Volatile变量间的顺序性,编译器不会进行乱序优化。(但CPU依然可能会乱序,x86和amd只开启了 store-load ooo)

volatile object - an object whose type is volatile-qualified, or a subobject of a volatile object, or a mutable subobject of a const-volatile object. Every access (read or write operation, member function call, etc.) made through a glvalue expression of volatile-qualified type is treated as a visible side-effect for the purposes of optimization (that is, within a single thread of execution, volatile accesses cannot be optimized out or reordered with another visible side effect that is sequenced-before or sequenced-after the volatile access. This makes volatile objects suitable for communication with a signal handler, but not with another thread of execution, see std::memory_order). Any attempt to refer to a volatile object through a non-volatile glvalue (e.g. through a reference or pointer to non-volatile type) results in undefined behavior.

C/C++ and "volatile"

When writing single-threaded code, declaring a variable “volatile” can be very useful. The compiler will not omit or reorder accesses to volatile locations. Combine that with the sequential consistency provided by the hardware, and you’re guaranteed that the loads and stores will appear to happen in the expected order.

However, accesses to volatile storage may be reordered with non-volatile accesses, so you have to be careful in multi-threaded uniprocessor environments (explicit compiler reorder barriers may be required). There are no atomicity guarantees, and no memory barrier provisions, so “volatile” doesn’t help you at all in multi-threaded SMP environments. The C and C++ language standards are being updated to address this with built-in atomic operations.

If you think you need to declare something “volatile”, that is a strong indicator that you should be using one of the atomic operations instead. 

Java版读写锁(来自打驰哥) https://www.cnblogs.com/DarrenChan/p/8619476.html
防止写者饿死精要:
写者到来的时候,先m_writeCount++,再判断是否读者为0,并且未出于写的过程中;(直接判断m_writeCount == 1是否可行?)
读者到来的时候,先检查m_writeCount

C++版本

  • RWLock.h
#ifndef RWLOCK__H
#define RWLOCK__H

#ifndef __cplusplus
#    error ERROR: This file requires C++ compilation(use a .cpp suffix)
#endif

#include <mutex>
#include <condition_variable>

namespace linduo {

class RWLock {
 public:
    RWLock();
    virtual ~RWLock() = default;

    void lockWrite();
    void unlockWrite();
    void lockRead();
    void unlockRead();

 private:
    volatile int m_readCount;
    volatile int m_writeCount;
    volatile bool m_isWriting;
    std::mutex m_Lock;
    std::condition_variable m_readCond;
    std::condition_variable m_writeCond;
};

class ReadGuard {
 public:
    explicit ReadGuard(RWLock& lock);
    virtual ~ReadGuard();

 private:
    ReadGuard(const ReadGuard&);
    ReadGuard& operator=(const ReadGuard&);

 private:
    RWLock &m_lock;
};


class WriteGuard {
 public:
    explicit WriteGuard(RWLock& lock);
    virtual ~WriteGuard();

 private:
    WriteGuard(const WriteGuard&);
    WriteGuard& operator=(const WriteGuard&);

 private:
  RWLock& m_lock;
};

} /* namespace linduo */
#endif  // RWLOCK__H
  • RWLock.cpp
#include "RWLock.h"

namespace linduo {

RWLock::RWLock()
    : m_readCount(0)
    , m_writeCount(0)
    , m_isWriting(false) {
    }

void RWLock::lockRead() {
    std::unique_lock<std::mutex> gurad(m_Lock);
    m_readCond.wait(gurad, [=] { return 0 == m_writeCount; });
    ++m_readCount;
}

void RWLock::unlockRead() {
    std::unique_lock<std::mutex> gurad(m_Lock);
    if (0 == (--m_readCount)
        && m_writeCount > 0) {
        // One write can go on
        m_writeCond.notify_one();
    }
}

void RWLock::lockWrite() {
    std::unique_lock<std::mutex> gurad(m_Lock);
    ++m_writeCount;
    m_writeCond.wait(gurad, [=] { return (0 == m_readCount) && !m_isWriting; });
    m_isWriting = true;
}

void RWLock::unlockWrite() {
    std::unique_lock<std::mutex> gurad(m_Lock);
    m_isWriting = false;
    if (0 == (--m_writeCount)) {
        // All read can go on
        m_readCond.notify_all();
    } else {
        // One write can go on
        m_writeCond.notify_one();
    }
}

ReadGuard::ReadGuard(RWLock &lock)
    : m_lock(lock) {
    m_lock.lockRead();
}

ReadGuard::~ReadGuard() {
    m_lock.unlockRead();
}

WriteGuard::WriteGuard(RWLock &lock)
    : m_lock(lock) {
    m_lock.lockWrite();
}

WriteGuard::~WriteGuard() {
    m_lock.unlockWrite();
}

} /* namespace linduo */
  • 使用
RWLock m_Lock;

void Wfunc() {
   // 写锁
   WriteGuard autoSync(m_Lock);
}

void Rfunc() {
  // 读锁
  ReadGuard autoSync(m_Lock);
}

以上是关于读写锁的实现的主要内容,如果未能解决你的问题,请参考以下文章

ZooKeeper实现读写锁

读写锁的实现

读写锁的实现

锁的封装 读写锁lock

只有一个底层锁的读写锁?

并发编程读写锁的实现原理(RWMutex)