Go Quiz: 從Go面試題看鎖的注意事項

coding進階發表於2022-03-12

背景

Google工程師Valentin Deleplace出了2道關於鎖的題目,拿出來和大家分享下。

題目1

// quiz_lock1.go
package main

import (
    "fmt"
    "sync"
)

func main() {
    var m sync.Mutex
    fmt.Print("1, ")
    m.Lock()
    m.Lock()
    m.Unlock()
    fmt.Println("2")
}
  • A: 1, 2
  • B: 1,
  • C: 1, fatal error:......
  • D: 編譯報錯

題目2

// quiz_lock2.go
package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    var m sync.Mutex
    fmt.Print("1, ")
    m.Lock()

    go func() {
        time.Sleep(200 * time.Millisecond)
        m.Unlock()
    }()

    m.Lock()
    fmt.Println("2")
}
  • A: 1, 2
  • B: 1,
  • C: 1, fatal error:......
  • D: 編譯報錯

解析

Go語言裡的sync.Mutex和sync.RWMutex都是不可重入的,Go語言裡沒有可重入鎖(也叫遞迴鎖)。

如果mutex沒有釋放,在同一個goroutine就不能對這個mutex加2次鎖,否則第2次加鎖就會阻塞,如果沒有其它goroutine去釋放這個mutex,就會導致死鎖,出現runtime error: fatal error: all goroutines are asleep - deadlock!

同時,sync.Mutex和sync.RWMutex允許一個goroutine對其加鎖,其它goroutine對其解鎖,不要求加鎖和解鎖在同一個goroutine裡。

所以第一道題目的答案是C,第二道題目的答案是A

思考題

// quiz_lock3.go

package main

import (
    "fmt"
    "sync"
)

var a sync.Mutex

func main() {
    a.Lock()
    fmt.Print("1, ")
    a.Unlock()
    fmt.Print("2, ")
    a.Unlock()
    fmt.Println("3")
}
  • A: 1, 2, 3
  • B: 1, 2, fatal error:......
  • C: 1, 2
  • D: 編譯報錯

想知道答案的可以給公眾號傳送訊息mutex獲取答案。

總結

Go語言裡的鎖和C++,Java裡的不太一樣,給大家總結了以下注意事項

  • Go的鎖是不可重入的,沒有遞迴鎖
  • 允許一個goroutine加鎖,另一個goroutine解鎖,不要求加鎖和解鎖在同一個goroutine裡
  • sync.Mutex的零值是沒有加鎖的Mutex,sync.RWMutex的零值是沒有加鎖的RWMutex
  • 更多細節可以參考References裡Mutex和RWMutex的官方說明

開源地址

文章和示例程式碼開源在GitHub: Go語言初級、中級和高階教程

公眾號:coding進階。關注公眾號可以獲取最新Go面試題和技術棧。

個人網站:Jincheng's Blog

知乎:無忌

References

相關文章