RWMutex

作者: wayyyy | 来源:发表于2022-04-05 00:56 被阅读0次

    前面分析了互斥锁,在针对写少读多的场景,更好的选择是使用读写锁。实现读写锁主要解决下列的问题:

    • 写锁需要阻塞写锁:一个协程拥有写锁时,其他协程写锁定需要阻塞
    • 写锁需要阻塞读锁:一个协程拥有写锁时,其他协程读锁定需要阻塞
    • 读锁需要阻塞写锁:一个协程拥有读锁时,其他协程写锁定需要阻塞
    • 读锁不能阻塞读锁:一个协程拥有读锁时,其他协程也可以拥有读锁
    读写锁数据数据结构

    源码中定义了读写锁的数据结构:

    type RWMutex struct {
        w           Mutex      //  用于控制多个写锁,获得写锁首先需要获取该锁
        writerSem   uint32     // 写阻塞等待的信号量,最后一个读者释放锁时会释放信号量
        readerSem   uint32     // 读阻塞的协程等待的信号量,持有写锁的协程释放锁后会释放信号量
        readerCount int32      // 记录读者的个数
        readerWait  int32      // 记录写阻塞时的读者个数
    }
    

    RWMutex 对外提供4个简单接口:

    • RLock 读锁定
    • RULock 解除读锁定
    • Lock 写锁定
    • Unlock 解除写锁定
    Lock 实现逻辑

    写锁定操作需要做2件事:

    1. 获取互斥锁
    2. 阻塞等待所有读操作结束
    image.png
    • 写操作是如何阻止写操作?
      读写锁里面包含一个互斥锁w,写锁定必须先获取该互斥锁,如果互斥锁已经被协程A获取,则协程B只能阻塞等待该互斥锁。

    • 读操作如何阻止写操作?
      读锁定会先将readerCount的值加1(见下面),此时写操作到来时发现读者数量不为0,会阻塞等待所有读操作结束。

    Unlock 实现逻辑

    解除写锁定需要做2件事:

    1. 唤醒因读锁定而被阻塞的协程
    2. 解除互斥锁,如果有其他协程写锁定操作,将被唤醒
    image.png
    RLock 实现逻辑

    读锁定操作需要做2件事:

    1. 增加读操作计数
    2. 阻塞等待写操作计数
    image.png
    • 写操作如何阻止读操作?
      readerCount 是一个整型值,用于表示读者数量,在不考虑写操作的情况下,每次读锁定将该值加1,每次解除锁定将该值减1,所以 readerCount 的取值为[0, N],N为读者个数,实际上最大可支持 2^30 个并发读者
      当进行写锁定时,会先将 readerCount 减去 2^30 ,从而 readerCount 变成了负值,此时再有读锁定到来时检测到为负值,便知道有写操作在进行,只好阻塞等待。
    RUlock 实现逻辑

    解除读锁定需要做2件事:

    1. 减少读操作计数
    2. 唤醒等待写操作的协程
    image.png
    为什么写锁定不会被饿死 ?

    写操作要等待读操作结束后才可以获取到锁,写操作等待期间可能还有新的读操作持续到来,如果写操作要等所有读操作结束,则很可能会被饿死?那是怎么解决这个问题呢?

    写操作到来时,会把readerCount的值复制到readerWait中,用于标记排在写操作前面的读者个数。
    前面的读操作结束后,除了会递减readerCount的值,还会递减readerWait的值,当readerWait的值变为0时唤醒写操作,写操作结束后再唤醒后面的读操作。

    相关文章

      网友评论

          本文标题:RWMutex

          本文链接:https://www.haomeiwen.com/subject/ojcixrtx.html