Bootstrap

【Golang 面试题】每日 3 题(三十)

✍个人博客:Pandaconda-CSDN博客
📣专栏地址:http://t.csdnimg.cn/UWz06
📚专栏简介:在这个专栏中,我将会分享 Golang 面试中常见的面试题给大家~
❤️如果有收获的话,欢迎点赞👍收藏📁,您的支持就是我创作的最大动力💪

88. Go 读写锁是什么?

概念:

读写互斥锁 RWMutex,是对 Mutex 的一个扩展,当一个 goroutine 获得了读锁后,其他 goroutine 可以获取读锁,但不能获取写锁;当一个 goroutine 获得了写锁后,其他 goroutine 既不能获取读锁也不能获取写锁(只能存在一个写者或多个读者,可以同时读)。

使用场景:

读多于写的情况(既保证线程安全,又保证性能不太差)。

89. Go 读写锁底层实现结构

互斥锁对应的是底层结构是 sync.RWMutex 结构体,,位于 src/sync/rwmutex.go 中

type RWMutex struct {
    w           Mutex  // 复用互斥锁
    writerSem   uint32 // 信号量,用于写等待读
    readerSem   uint32 // 信号量,用于读等待写
    readerCount int32  // 当前执行读的 goroutine 数量
    readerWait  int32  // 被阻塞的准备读的 goroutine 的数量
}

操作:
读锁的加锁与释放

func (rw *RWMutex) RLock() // 加读锁
func (rw *RWMutex) RUnlock() // 释放读锁

90. Go 读锁相关操作

加读锁

func (rw *RWMutex) RLock() {
// 为什么readerCount会小于0呢?往下看发现writer的Lock()会对readerCount做减法操作(原子操作)
  if atomic.AddInt32(&rw.readerCount, 1) < 0 {
    // A writer is pending, wait for it.
    runtime_Semacquire(&rw.readerSem)
  }
}

atomic.AddInt32(&rw.readerCount, 1) 调用这个原子方法,对当前在读的数量加 1,如果返回负数,那么说明当前有其他写锁,这时候就调用 runtime_SemacquireMutex 休眠当前 goroutine 等待被唤醒。

释放读锁

解锁的时候对正在读的操作减 1,如果返回值小于 0 那么说明当前有在写的操作,这个时候调用 rUnlockSlow 进入慢速通道。

func (rw *RWMutex) RUnlock() {
    if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
        rw.rUnlockSlow(r)
    }
}

被阻塞的准备读的 goroutine 的数量减 1,readerWait 为 0,就表示当前没有正在准备读的 goroutine 这时候调用 runtime_Semrelease 唤醒写操作。

func (rw *RWMutex) rUnlockSlow(r int32) {
    // A writer is pending.
    if atomic.AddInt32(&rw.readerWait, -1) == 0 {
        // The last reader unblocks the writer.
        runtime_Semrelease(&rw.writerSem, false, 1)
    }
}

写锁的加锁与释放。

func (rw *RWMutex) Lock() // 加写锁
func (rw *RWMutex) Unlock() // 释放写锁
;