Go 源码之读写锁 sync.RWMutex
文章目录
- Go 源码之读写锁 sync.RWMutex
- 一、简介
- 二、源码
- (一)RWMutex数据结构
- (二)Lock
- (三)Unlock
- (四)TryRLock
- (五)Rlock
- (六)RUnlock
- 三、常见问题
- 1. 什么是CAS,什么是原子操作
- 2. 写操作是如何阻止写操作的
- 3. 写操作是如何阻止读操作的
- 4. 读操作是如何阻止写操作的
- 5. 为什么写锁定不会被饿死
- 总结
- 参考资料
一、简介
sync.RWMutex 是 Go 语言标准库中的读写锁。
读写锁允许同时存在读锁和写锁。读锁可以被多个线程同时持有,而写锁在任何时候只能被一个线程持有。
sync.RWMutex 的主要作用:是在多线程环境下提供对共享资源的读/写访问控制,以提高并发性能。
它的一些主要方法包括:
- Lock:获取写锁。
- RLock:获取读锁。
- Unlock:释放锁。
使用读写锁的好处是,在多读少写的场景下,可以提高并发性能,因为读操作不会相互阻塞。
在使用 sync.RWMutex 时,需要注意以下几点:
- 确保在适当的时候释放锁,以避免死锁。
- 避免在持有锁的情况下进行耗时操作。
- 尽量减少锁的持有时间
二、源码
(一)RWMutex数据结构
type RWMutex struct {w Mutex // 写锁writerSem uint32 // 缓冲信号量,获取写锁的阻塞等待信号队列readerSem uint32 // 缓冲信号量,获取读锁的阻塞等待信号队列readerCount int32 // 当前持有读锁的 goroutine 数量,负数表示有个写锁在执行readerWait int32 // 获取写锁时,如果之前还有 readerWait 数量的读锁在执行,则需要等待执行完才能获取写锁
}
字段说明:
-
w
写锁
-
writerSem
缓冲信号量,当有goroutine获取写锁时,如果当前有读锁在占有,则调用 runtime_SemacquireMutex(&rw.writerSem, false, 0)
将当前goroutine进行睡眠,并排队到 writerSem 队列的队尾,等待所有的读锁释放之后再调用runtime_Semrelease(&rw.writerSem, false, 1)进行唤醒
-
readerSem
缓冲信号量,当有goroutine获取读锁时,如果当前有写锁在占有(readerCount),则调用 runtime_SemacquireMutex(&rw.readerSem, false, 0),将当前goroutine进行睡眠,并排队到 readerSem 队列的队尾,等待写锁释放之后再调用runtime_Semrelease(&rw.readerSem, false, 0)进行唤醒
-
readerCount
当前持有读锁的goroutine数量,负数表示有个写锁在执行,在获取写锁时,会将readerCount-rwmutexMaxReaders变为负数
写锁释放后readerCount会再+rwmutexMaxReaders变为正数
-
readerWait
首先读锁是会阻塞写锁的获取的,当一个goroutine尝试去获取一个写锁时,会将当前持有读锁的数量readerCount赋值给readerWait,表示当前goroutine要等待readerWait个goroutine释放读锁之后才能成功获取写锁
(二)Lock
// 获取写锁,会等待所有的读锁释放
func (rw *RWMutex) Lock() {if race.Enabled {_ = rw.w.staterace.Disable()}