读写锁(RWMutex) · golang · 看云
### 说明
1. 信号量
其本质是一个整数,并关联两个操作:
申请acquire(也称为 wait、decrement 或 P 操作)
释放release(也称 signal、increment 或 V 操作)
### 锁的定义
```go
// A Locker represents an object that can be locked and unlocked.
type Locker interface {
Lock()
Unlock()
}
```
在goalng中如果实现了Lock和Unlock方法,那么它就可以被称为锁。
#### RWMutex 的结构
```
~~~
type RWMutex struct {
w Mutex // 控制多个写入的互斥量
writerSem uint32 // 写入操作的信号量
readerSem uint32 // 读取操作的信号量
readerCount int32 // 当前读操作数
readerWait int32 // 写入操作等待读操作数
}
~~~
```
主要方法是下面标注的 4 个
![](https://img.kancloud.cn/3c/5d/3c5db3e3e12e3872d0c1eedc20a88ca4_768x778.png)
有一个常量:
~~~
const rwmutexMaxReaders = 1 << 30
~~~
### 1. 首先是申请读锁
~~~
func (rw *RWMutex) RLock() {
if race.Enabled {
_ = rw.w.state
race.Disable()
}
if atomic.AddInt32(&rw.readerCount, 1) < 0 {
// A writer is pending, wait for it.
runtime_SemacquireMutex(&rw.readerSem, false, 0)
}
if race.Enabled {
race.Enable()
race.Acquire(unsafe.Pointer(&rw.readerSem))
}
}
~~~
忽略前4行的竞态检测;
当有协程申请读锁时,readerCount 就会加一,如果小于 0,就会等待写锁释放(结合申请写锁部分就明白为什么是小于 0);
~~~
runtime_SemacquireMutex //这个方法是一个runtime的方法,会一直等待传入的s出现>0的时候
~~~
### 释放读锁
~~~
// RUnlock undoes a single RLock call;
// it does not affect other simultaneous readers.
// It is a run-time error if rw is not locked for reading
// on entry to RUnlock.
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 {
// Outlined slow-path to allow the fast-path to be inlined
rw.rUnlockSlow(r)
}
if race.Enabled {
race.Enable()
}
}
~~~
主要逻辑中,readerCount 减一,如果值小于 0,调用了另一个方法(正常使用释放读锁不会出现小于 0 的情况):
~~~
func (rw *RWMutex) rUnlockSlow(r int32) {
if r+1 == 0 || r+1 == -rwmutexMaxReaders {
race.Enable()
throw("sync: RUnlock of unlocked RWMutex")
}
// A writer is pending.
if atomic.AddInt32(&rw.readerWait, -1) == 0 {
// The last reader unblocks the writer.
runtime_Semrelease(&rw.writerSem, false, 1)
}
}
~~~
r + 1 的值等于 0,说明在没有加读锁的情况下申请了释放读锁;
r + 1 的值等于常量值,说明在申请写锁后释放读锁(参考申请写锁);