什么是sync.RWMutex
首先看一下RWMutex
的定义
RWMutex
是读/写互斥锁。锁可以由任意数量的读者或单个写者持有。RWMutex
的零值是未锁定的互斥锁
这个package
包含了4个方法
func (rw *RWMutex) Lock // Lock locks rw for writing.
func (rw *RWMutex) RLock // RLock locks rw for reading.
func (rw *RWMutex) RUnlock // Unlock locks rw for reading
func (rw *RWMutex) Unlock // Unlock unlocks rw for writing
复制代码
本次来介绍一下sync.RWMutex
的实现,还是先举例
var mu sync.RWMutex
var data map[string]string
func main() {
data = map[string]string{"hoge": "fuga"}
mu = sync.RWMutex{}
go read()
go read()
go write()
go read()
time.Sleep(5 * time.Second)
}
// 读方法
func read() {
println("read_start")
mu.RLock()
defer mu.RUnlock()
time.Sleep(1*time.Second)
println("read_complete", data["hoge"])
}
// 写方法
func write() {
println("write_start")
//mu.Lock() 仔细看下这两行代码,此处是注释掉的
//defer mu.Unlock()
time.Sleep(2 * time.Second)
data["hoge"] = "piyo"
println("write_complete")
}
复制代码
注释掉上述两行代码会输出什么呢?
read_start
read_start
write_start
read_start
read_complete fuga
read_complete fuga
read_complete fuga
write_complete
复制代码
如果把上述代码注释掉的地方打开结果会有影响吗?答案是肯定的!
read_start
write_start
read_start
read_start
read_complete fuga
write_complete
// 可以看到 read_complete已经被write的字串替换掉了
read_complete piyo
read_complete piyo
复制代码
此时就需要讲解下sync.RWMutex
锁的实现了,先看下RWMutex
的定义
type RWMutex struct {
w Mutex // held if there are pending writers
writerSem uint32 // semaphore(信号) for writers to wait for completing readers
readerSem uint32 // semaphore for readers to wait for completing writers
readerCount int32 // number of pending readers
readerWait int32 // number of departing(离开) readers
}
复制代码
RLock & RUnlock
读锁 & 释放读锁
设定最大的读锁数量
const rwmutexMaxReaders = 1 << 30
RLock锁定rw进行阅读。
它不应该用于递归读锁定;被阻止的锁调用排除新读者获取锁定。
func (rw *RWMutex) RLock() {
if race.Enabled {
_ = rw.w.state
race.Disable()
}
if atomic.AddInt32(&rw.readerCount, 1) < 0 {
// 一个写者正在等待,waiting...
runtime_SemacquireMutex(&rw.readerSem, false)
}
// 静态检测
if race.Enabled {
race.Enable()
race.Acquire(unsafe.Pointer(&rw.readerSem))
}
}
复制代码
此处atomic.AddInt32(&rw.readerCount,1)
执行的是原子加操作,此时有两种场景:
- 此时无写锁 (
readerCount
+ 1) > 0RLock
正常结束 - 此时有写锁 (
readerCount
+ 1) < 0 等待写锁结束
RUnlock撤销单个RLock调用;它不会影响其他同时阅读的读者。
如果在进入RUnlock时没有锁定rw进行读取,则会出现运行时错误。
func (rw *RWMutex) RUnlock() {
if race.Enabled {
_ = rw.w.state
race.ReleaseMerge(unsafe.Pointer(&rw.writerSem))
race.Disable()
}
if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
if r+1 == 0 || r+1 == -rwmutexMaxReaders {
race.Enable()
// 报错,解锁了一个未锁的RWMutex
throw("sync: RUnlock of unlocked RWMutex")
}
// 如果此时没有读者等待
if atomic.AddInt32(&rw.readerWait, -1) == 0 {
// The last reader unblocks the writer.
runtime_Semrelease(&rw.writerSem, false)
}
}
if race.Enabled {
race.Enable()
}
}
复制代码
runtime_SemacquireMutex
函数是goroutine
等待队列的enqueue
runtime_Semrelease
函数是goroutine
等待队列的dequeue
操作
atomic.AddInt32(&rw.readerCount, -1)
此处也有几种场景
(ps
:此处只考虑单个读写锁)
锁 | 读锁&写锁 | 写锁 | 读锁 |
---|---|---|---|
readerCount | -(1<<30) | panic | 0 |
readerWait | 0 | panic | 0 |
Lock & UnLock
写锁&释放写锁
Lock锁定用于写入。如果锁已经锁定以进行读取或写入,则Lock将锁定,直到锁定可用。
func (rw *RWMutex) Lock() {
// 静态检测
if race.Enabled {
_ = rw.w.state
race.Disable()
}
// 首先,与其他写者解决竞争问题(这里采用了互斥锁)
rw.w.Lock()
// 如果一个写者得到了锁,readerCount = - (1<<30) , r = 0
r := atomic.AddInt32(&rw.readerCount, -rwmutexMaxReaders) + rwmutexMaxReaders
// 等待活跃的读者
// r!=0说明有读者加锁,此处需等待
if r != 0 && atomic.AddInt32(&rw.readerWait, r) != 0 {
runtime_SemacquireMutex(&rw.writerSem, false)
}
if race.Enabled {
race.Enable()
race.Acquire(unsafe.Pointer(&rw.readerSem))
race.Acquire(unsafe.Pointer(&rw.writerSem))
}
}
func (rw *RWMutex) Unlock() {
// 静态检测
if race.Enabled {
_ = rw.w.state
race.Release(unsafe.Pointer(&rw.readerSem))
race.Disable()
}
// Announce to readers there is no active writer.
r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
if r >= rwmutexMaxReaders {
race.Enable()
throw("sync: Unlock of unlocked RWMutex")
}
// 循环等待读者完成
for i := 0; i < int(r); i++ {
runtime_Semrelease(&rw.readerSem, false)
}
// Allow other writers to proceed.(继续进行)
rw.w.Unlock()
if race.Enabled {
race.Enable()
}
}
复制代码
锁 | 读锁&写锁 | 写锁 | 读锁 |
---|---|---|---|
readerCount | 1 | 0 | panic |
readerWait | 1 | 0 | panic |
that's all
有疑问加站长微信联系(非本文作者)