✍个人博客:Pandaconda-CSDN博客
📣专栏地址:http://t.csdnimg.cn/UWz06
📚专栏简介:在这个专栏中,我将会分享 Golang 面试中常见的面试题给大家~
❤️如果有收获的话,欢迎点赞👍收藏📁,您的支持就是我创作的最大动力💪
85. RWMutex 实现
sync.RWMutex 是 Go 语言标准库提供的一种读写锁,用于在多个 goroutine 同时访问共享资源时进行保护。与 sync.Mutex 类似,sync.RWMutex 也是通过互斥锁实现的,但是它允许多个 goroutine 同时获取读锁,而只允许一个 goroutine 获取写锁。
下面是一个简单的 sync.RWMutex 的实现示例:
type RWMutex struct {writerSem chan struct{} // 写者用的信号量readerSem chan struct{} // 读者用的信号量readerCount int // 当前持有读锁的goroutine数量writerCount int // 当前持有写锁的goroutine数量readerWait int // 正在等待读锁的goroutine数量writerWait int // 正在等待写锁的goroutine数量writerLocked bool // 是否有goroutine持有写锁
}
func NewRWMutex() *RWMutex {return &RWMutex{writerSem: make(chan struct{}, 1),readerSem: make(chan struct{}, 1),}
}
// 获取读锁
func (m *RWMutex) RLock() {// 获取读锁的过程需要加锁m.writerSem <- struct{}{} // 防止写者获取锁m.readerSem <- struct{}{} // 获取读锁的信号量// 更新状态m.readerCount++if m.writerLocked || m.writerWait > 0 {m.readerWait++<-m.readerSem // 等待写者释放锁m.readerWait--}// 释放加锁时获取的信号量<-m.writerSem
}
// 释放读锁
func (m *RWMutex) RUnlock() {// 获取读锁的过程需要加锁m.writerSem <- struct{}{} // 防止写者获取锁// 更新状态m.readerCount--if m.readerCount == 0 && m.writerWait > 0 {<-m.writerSem // 优先唤醒写者}// 释放加锁时获取的信号量<-m.writerSem
}
// 获取写锁
func (m *RWMutex) Lock() {// 获取写锁的过程需要加锁m.writerSem <- struct{}{} // 防止其他写者获取锁m.writerWait++// 等待其他goroutine释放读锁或写锁for m.writerLocked || m.readerCount > 0 {<-m.readerSem}// 更新状态m.writerWait--m.writerLocked = true// 释放加锁时获取的信号量<-m.writerSem
}
// 释放写锁
func (m *RWMutex) Unlock() {// 获取写锁的过程需要加锁m.writerSem <- struct{}{}// 更新状态m.writerLocked = falseif m.writerWait > 0 {<-m.writerSem} else if m.readerWait > 0 {for i := 0; i < m.readerCount; i++ {m.readerSem <- struct{}{} // 优先唤醒读者}}// 释放加锁时获取的信号量<-m.writerSem
}
86. RWMutex 的实现中包含了什么内容?
在这个实现中,sync.RWMutex 包含以下成员:
- writerSem 和 readerSem:两个用于同步的信号量通道。写锁会在 writerSem 上等待,读锁会在 readerSem 上等待。
- readerCount 和 writerCount:当前持有读锁和写锁的 goroutine 数量。
- readerWait 和 writerWait:正在等待读锁和写锁的 goroutine 数量。
- writerLocked:标记当前是否有 goroutine 持有写锁。
在读锁和写锁获取和释放的过程中,都需要先获取 writerSem 信号量防止其他写者获取锁。获取读锁时还需要获取 readerSem 信号量,而获取写锁时需要等待其他 goroutine 释放读锁或写锁。
这个实现中有两个重要的细节:
- 优先唤醒写者:在释放读锁或写锁时,如果有正在等待的写锁 goroutine,应该优先唤醒它们,因为写锁的优先级更高。
- 读锁的等待问题:在等待读锁的 goroutine 中,如果有其他 goroutine 正在持有写锁或等待写锁,那么这些读锁 goroutine 应该等待写锁 goroutine 释放锁,避免因等待读锁而导致写锁饥饿。
87. RWMutex 注意事项
- RWMutex 是单写多读锁,该锁可以加多个读锁或者一个写锁。
- 读锁占用的情况下会阻止写,不会阻止读,多个 Goroutine 可以同时获取读锁。
- 写锁会阻止其他 Goroutine(无论读和写)进来,整个锁由该 Goroutine 独占。
- 适用于读多写少的场景。
- RWMutex 类型变量的零值是一个未锁定状态的互斥锁。
- RWMutex 在首次被使用之后就不能再被拷贝。
- RWMutex 的读锁或写锁在未锁定状态,解锁操作都会引发 panic。
- RWMutex 的一个写锁去锁定临界区的共享资源,如果临界区的共享资源已被(读锁或写锁)锁定,这个写锁操作的 goroutine 将被阻塞直到解锁。
- RWMutex 的读锁不要用于递归调用,比较容易产生死锁。
- RWMutex 的锁定状态与特定的 goroutine 没有关联。一个 goroutine 可以 RLock(Lock),另一个 goroutine 可以 RUnlock(Unlock)。
- 写锁被解锁后,所有因操作锁定读锁而被阻塞的 goroutine 会被唤醒,并都可以成功锁定读锁。
- 读锁被解锁后,在没有被其他读锁锁定的前提下,所有因操作锁定写锁而被阻塞的 Goroutine,其中等待时间最长的一个 Goroutine 会被唤醒。