golang鎖機制


Golang中如何避免死鎖:加鎖

  • 讀寫鎖中的可讀鎖(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()。(這是個注意的地方)

否則,會 panic fatal 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 輸出就一定是沒發生。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM