sync.RWMutex
首先看一下RWMutex
的定義golang
RWMutex
是讀/寫互斥鎖。鎖能夠由任意數量的讀者或單個寫者持有。RWMutex
的零值是未鎖定的互斥鎖bash
這個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
的實現,仍是先舉例ui
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")
}
複製代碼
註釋掉上述兩行代碼會輸出什麼呢?atom
read_start
read_start
write_start
read_start
read_complete fuga
read_complete fuga
read_complete fuga
write_complete
複製代碼
若是把上述代碼註釋掉的地方打開結果會有影響嗎?答案是確定的!spa
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
的定義code
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) > 0 RLock
正常結束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
string