golang鎖機制
阿新 • • 發佈:2020-11-17
- 讀寫鎖中的可讀鎖(
sync.RWMutex
的RLock()
)可以巢狀使用的。 - 互斥鎖(
sync.Mutex
和sync.RWMutex
的Lock()
)是不可以互相巢狀的,且不可以與可讀鎖巢狀。
之前我在讀寫鎖和互斥鎖上理解有偏差,認為讀寫鎖與互斥鎖是完全獨立且相互對應的關係。現在理解為互斥
只是一種特性。而把sync.Mutex
叫作全域性鎖,sync.RWMutex
叫作讀寫鎖。
全域性鎖sync.Mutex
,是同一時刻某一資源只能上一個鎖,此鎖具有排他性,上鎖後只能被此執行緒使用,直至解鎖。加鎖後即不能讀也不能寫。全域性鎖是互斥鎖,即sync.Mutex
是個互斥鎖。
讀寫鎖sync.RWMutex
,將使用者分為讀者和寫者兩個概念,支援同時多個讀者一起讀共享資源,但寫時只能有一個,並且在寫時不可以讀。理論上來說,sync.RWMutex
的Lock()
也是個互斥鎖。
踩坑點
將上面的結論展開一下,更清晰得說(為避免理解偏差寧可嘮叨一些):
sync.Mutex
的鎖是不可以巢狀使用的。sync.RWMutex
的mu.Lock()
是不可以巢狀的。sync.RWMutex
的mu.Lock()
中不可以巢狀mu.RLock()
。(這是個注意的地方)
否則,會 panicfatal error: all goroutines are asleep - deadlock!
所以以下函式不會造成 panic:
var l sync.RWMutex func readAndRead() { // 可讀鎖內使用可讀鎖 l.RLock() defer l.RUnlock() l.RLock() defer l.RUnlock() } func main() { lockAndRead() time.Sleep(5 * time.Second) }
而將readAndRead
換為以下三種函式均會造成 panic:
func lockAndLock() { // 全域性鎖內使用全域性鎖 l.Lock() defer l.Unlock() l.Lock() defer l.Unlock() } func lockAndRead() {// 全域性鎖內使用可讀鎖 l.Lock() defer l.Unlock() // 由於 defer 是棧式執行,所以這兩個鎖是巢狀結構 l.RLock() defer l.RUnlock() } func readAndLock() { // 可讀鎖內使用全域性鎖 l.RLock() defer l.RUnlock() l.Lock() defer l.Unlock() }
注: 在 goroutine 中的 panic 不會影響主程式,所以在測試時要注意並不是沒有 panic 輸出就一定是沒發生。