Go 語言提供兩類鎖: 互斥鎖(Mutex)和讀寫鎖(RWMutex)。
其中讀寫鎖(RWMutex)是基于互斥鎖(Mutex)實現(xiàn)的,我們看讀寫鎖的定義(sync/rwmutex.go):
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
}
讀寫鎖里面內(nèi)置了一個互斥鎖。
互斥鎖的使用
func (m *Mutex) Lock()
func (m *Mutex) Unlock()
互斥鎖提供兩個API,Lock和Unlock,按字面意思一個是加鎖,另一個是釋放鎖。
- 當(dāng)一個互斥鎖被Lock了以后,任何goroutines(不管是不是當(dāng)前Lock了這個鎖的goroutine)試圖再Lock這個鎖時都會被掛起,直到鎖被釋放Unlock。
- 互斥鎖不是可重入的,也就是說鎖被一個goroutine Lock了之后,當(dāng)前goroutine也不能再對當(dāng)前鎖進行Lock操作,否則也會被掛起。(如果當(dāng)前環(huán)境只有一個活動goroutine,go會報死鎖錯誤 fatal error: all goroutines are asleep - deadlock!)
- 因為go語言的Mutex并不和任何goroutine相關(guān)聯(lián),這需要對比Linux pthread里面的線程鎖的差異
- 正因為Mutex并不與特定的goroutine相關(guān)聯(lián),所以一個互斥鎖被一個goroutine鎖住了,它可以在另外一個goroutine里面被解鎖Unlock。
總之
- 一個已經(jīng)鎖住的互斥鎖不能再次被鎖住,不管是同一個還是另一個goroutine
- 一個已經(jīng)釋放的互斥鎖也不能再次被釋放,不管是同一個還是另一個goroutine
讀寫鎖
func (rw *RWMutex) RLock()
func (rw *RWMutex) RUnlock()
func (rw *RWMutex) Lock()
func (rw *RWMutex) Unlock()
和互斥鎖相比較,讀寫鎖增加了兩個RLock()/RUnLock(),我們把它定義為讀鎖,另一個相對就叫寫鎖。
讀寫鎖和pthread的的讀寫鎖類似,可以多人同時鎖住讀鎖,但是寫鎖只能有一個人鎖住。
- 當(dāng)讀寫鎖被一個goroutine加了讀鎖,此讀寫鎖還能被任何goroutine加讀鎖,但不能加寫鎖(會等待直到所有讀鎖釋放)
- 當(dāng)讀寫鎖被一個goroutine加了寫鎖,任何goroutine將不能再對此讀寫鎖加鎖,不管是加讀鎖還是加寫鎖
- 當(dāng)讀寫鎖被一個goroutine加了讀鎖,在同一個gorounte內(nèi),它不能再被加寫鎖,但可以加讀鎖;同樣
- 當(dāng)讀寫鎖被一個goroutine加了寫鎖,在同一個gorounte內(nèi),它不能再被加讀鎖,加寫鎖也不能。
因為RWMutex和Mutex一樣并不與特定的goroutine相關(guān)聯(lián),所有的鎖操作并不區(qū)分是哪一個goroutine進來的請求,是同一個goroutine還是不同的goroutine一同對待。
總結(jié)一點
- goroutine并不是和unix的thread一個概念,他們不具有一一對應(yīng)關(guān)系。
- go語言的鎖也并不與任何特定的goroutine相關(guān)聯(lián),鎖并沒有記錄當(dāng)前是哪個goroutine鎖住了自己。
可能是因為既然goroutine并不直接相關(guān)聯(lián)與一個thread,同一個thread可能關(guān)聯(lián)到多個goroutine(請閱讀goroutine模型的文章),所以鎖無法確切地區(qū)分是哪一個thread,所以也沒有辦法做到關(guān)聯(lián)。