這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
介紹
golang 中的 sync 包實現了兩種鎖:
- Mutex:互斥鎖
- RWMutex:讀寫鎖,RWMutex 基於 Mutex 實現
Mutex(互斥鎖)
- Mutex 為互斥鎖,Lock() 加鎖,Unlock() 解鎖
- 在一個 goroutine 獲得 Mutex 後,其他 goroutine 只能等到這個 goroutine 釋放該 Mutex
- 使用 Lock() 加鎖後,不能再繼續對其加鎖,直到利用 Unlock() 解鎖後才能再加鎖
- 在 Lock() 之前使用 Unlock() 會導致 panic 異常
- 已經鎖定的 Mutex 並不與特定的 goroutine 相關聯,這樣可以利用一個 goroutine 對其加鎖,再利用其他 goroutine 對其解鎖
- 在同一個 goroutine 中的 Mutex 解鎖之前再次進行加鎖,會導致死結
- 適用於讀寫不確定,並且只有一個讀或者寫的情境
樣本
加鎖和解鎖樣本
package mainimport ( "time" "fmt" "sync")func main() { var mutex sync.Mutex fmt.Println("Lock the lock") mutex.Lock() fmt.Println("The lock is locked") channels := make([]chan int, 4) for i := 0; i < 4; i++ { channels[i] = make(chan int) go func(i int, c chan int) { fmt.Println("Not lock: ", i) mutex.Lock() fmt.Println("Locked: ", i) time.Sleep(time.Second) fmt.Println("Unlock the lock: ", i) mutex.Unlock() c <- i }(i, channels[i]) } time.Sleep(time.Second) fmt.Println("Unlock the lock") mutex.Unlock() time.Sleep(time.Second) for _, c := range channels { <-c }}
程式輸出:
Lock the lockThe lock is lockedNot lock: 1Not lock: 2Not lock: 0Not lock: 3Unlock the lockLocked: 1Unlock the lock: 1Locked: 2Unlock the lock: 2Locked: 3Unlock the lock: 3Locked: 0Unlock the lock: 0
在解鎖之前加鎖會導致死結
package mainimport ( "fmt" "sync")func main(){ var mutex sync.Mutex mutex.Lock() fmt.Println("Locked") mutex.Lock()}
程式輸出:
Lockedfatal error: all goroutines are asleep - deadlock!
RWMutex(讀寫鎖)
- RWMutex 是單寫多讀鎖,該鎖可以加多個讀鎖或者一個寫鎖
- 讀鎖佔用的情況下會阻止寫,不會阻止讀,多個 goroutine 可以同時擷取讀鎖
- 寫鎖會阻止其他 goroutine(無論讀和寫)進來,整個鎖由該 goroutine 獨佔
- 適用於讀多寫少的情境
Lock() 和 Unlock()
- Lock() 加寫鎖,Unlock() 解寫鎖
- 如果在加寫鎖之前已經有其他的讀鎖和寫鎖,則 Lock() 會阻塞直到該鎖可用,為確保該鎖可用,已經阻塞的 Lock() 調用會從獲得的鎖中排除新的讀取器,即寫鎖許可權高於讀鎖,有寫鎖時優先進行寫鎖定
- 在 Lock() 之前使用 Unlock() 會導致 panic 異常
RLock() 和 RUnlock()
- RLock() 加讀鎖,RUnlock() 解讀鎖
- RLock() 加讀鎖時,如果存在寫鎖,則無法加讀鎖;當只有讀鎖或者沒有鎖時,可以加讀鎖,讀鎖可以載入多個
- RUnlock() 解讀鎖,RUnlock() 撤銷單詞 RLock() 調用,對於其他同時存在的讀鎖則沒有效果
- 在沒有讀鎖的情況下調用 RUnlock() 會導致 panic 錯誤
- RUnlock() 的個數不得多餘 RLock(),否則會導致 panic 錯誤
樣本
Lock() 和 Unlock()
package mainimport ( "sync" "fmt" "time")func main() { var mutex *sync.RWMutex mutex = new(sync.RWMutex) fmt.Println("Lock the lock") mutex.Lock() fmt.Println("The lock is locked") channels := make([]chan int, 4) for i := 0; i < 4; i++ { channels[i] = make(chan int) go func(i int, c chan int) { fmt.Println("Not lock: ", i) mutex.Lock() fmt.Println("Locked: ", i) fmt.Println("Unlock the lock: ", i) mutex.Unlock() c <- i }(i, channels[i]) } time.Sleep(time.Second) fmt.Println("Unlock the lock") mutex.Unlock() time.Sleep(time.Second) for _, c := range channels { <-c }}
程式輸出:
Lock the lockThe lock is lockedNot lock: 0Not lock: 1Not lock: 2Not lock: 3Unlock the lockLocked: 0Unlock the lock: 0Locked: 2Unlock the lock: 2Locked: 3Unlock the lock: 3Locked: 1Unlock the lock: 1
Lock() 和 RLock()
package mainimport ( "sync" "fmt" "time")func main() { var mutex *sync.RWMutex mutex = new(sync.RWMutex) fmt.Println("Lock the lock") mutex.Lock() fmt.Println("The lock is locked") channels := make([]chan int, 4) for i := 0; i < 4; i++ { channels[i] = make(chan int) go func(i int, c chan int) { fmt.Println("Not read lock: ", i) mutex.RLock() fmt.Println("Read Locked: ", i) fmt.Println("Unlock the read lock: ", i) time.Sleep(time.Second) mutex.RUnlock() c <- i }(i, channels[i]) } time.Sleep(time.Second) fmt.Println("Unlock the lock") mutex.Unlock() time.Sleep(time.Second) for _, c := range channels { <-c }}
程式輸出:
Lock the lockThe lock is lockedNot read lock: 2Not read lock: 3Not read lock: 1Not read lock: 0Unlock the lockRead Locked: 2Read Locked: 1Unlock the read lock: 2Unlock the read lock: 1Read Locked: 0Read Locked: 3Unlock the read lock: 0Unlock the read lock: 3
Unlock() 使用之前不存在 Lock()
package mainimport ( "sync")func main(){ var rwmutex *sync.RWMutex rwmutex = new(sync.RWMutex) rwmutex.Unlock()}
程式輸出:
panic: sync: Unlock of unlocked RWMutex
RWMutex 使用不當導致的死結
樣本1:
package mainimport ( "sync")func main(){ var rwmutex *sync.RWMutex rwmutex = new(sync.RWMutex) rwmutex.Lock() rwmutex.Lock()}
程式輸出:
fatal error: all goroutines are asleep - deadlock!
樣本2:
package mainimport ( "sync")func main(){ var rwmutex *sync.RWMutex rwmutex = new(sync.RWMutex) rwmutex.Lock() rwmutex.RLock()}
程式輸出:
fatal error: all goroutines are asleep - deadlock!
RUnlock() 之前不存在 RLock()
package mainimport ( "sync")func main(){ var rwmutex *sync.RWMutex rwmutex = new(sync.RWMutex) rwmutex.RUnlock()}
程式輸出:
panic: sync: RUnlock of unlocked RWMutex
RUnlock() 個數多於 RLock()
package mainimport ( "sync")func main(){ var rwmutex *sync.RWMutex rwmutex = new(sync.RWMutex) rwmutex.RLock() rwmutex.RLock() rwmutex.RUnlock() rwmutex.RUnlock() rwmutex.RUnlock()}
程式輸出:
panic: sync: RUnlock of unlocked RWMutex