1. 程式人生 > 實用技巧 >golang鎖機制

golang鎖機制

  • 讀寫鎖中的可讀鎖(sync.RWMutexRLock())可以巢狀使用的。
  • 互斥鎖(sync.Mutexsync.RWMutexLock())是不可以互相巢狀的,且不可以與可讀鎖巢狀。

之前我在讀寫鎖和互斥鎖上理解有偏差,認為讀寫鎖與互斥鎖是完全獨立且相互對應的關係。現在理解為互斥只是一種特性。而把sync.Mutex叫作全域性鎖sync.RWMutex叫作讀寫鎖

全域性鎖sync.Mutex,是同一時刻某一資源只能上一個鎖,此鎖具有排他性,上鎖後只能被此執行緒使用,直至解鎖。加鎖後即不能讀也不能寫。全域性鎖是互斥鎖,即sync.Mutex是個互斥鎖。

讀寫鎖sync.RWMutex

,將使用者分為讀者和寫者兩個概念,支援同時多個讀者一起讀共享資源,但寫時只能有一個,並且在寫時不可以讀。理論上來說,sync.RWMutexLock()也是個互斥鎖。

踩坑點

將上面的結論展開一下,更清晰得說(為避免理解偏差寧可嘮叨一些):

  • sync.Mutex的鎖是不可以巢狀使用的。
  • sync.RWMutexmu.Lock()是不可以巢狀的。
  • sync.RWMutexmu.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 輸出就一定是沒發生。