Go 實例教學:互斥鎖

在前一個實例中,我們看到如何以 原子化運算子 管理單純的計數器狀態。對於更複雜的狀態,我們可以使用 互斥鎖,跨多個 goroutine 安全地存取資料。

package main
import (
    "fmt"
    "sync"
)

Container 包含一個計數器對映;由於我們想要讓多個 goroutine 同時更新它,因此我們加入一個 Mutex 來同步存取。注意互斥鎖不能複製,因此如果這個 struct 傳遞,應該用指標傳遞。

type Container struct {
    mu       sync.Mutex
    counters map[string]int
}

在存取 `counters` 前鎖住互斥鎖;在函式尾端使用 defer 語句解鎖。

func (c *Container) inc(name string) {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.counters[name]++
}

注意互斥鎖的零值可以直接使用,所以這裡不需要初始化。

func main() {
    c := Container{
        counters: map[string]int{"a": 0, "b": 0},
    }
    var wg sync.WaitGroup

此函式會在迴圈中增加一個命名計數器。

    doIncrement := func(name string, n int) {
        for i := 0; i < n; i++ {
            c.inc(name)
        }
        wg.Done()
    }

同時執行數個 goroutine;注意它們都存取同一個 Container,其中兩個存取同一個計數器。

    wg.Add(3)
    go doIncrement("a", 10000)
    go doIncrement("a", 10000)
    go doIncrement("b", 10000)

等待 goroutine 執行完畢

    wg.Wait()
    fmt.Println(c.counters)
}

執行程式後會顯示計數器更新為預期結果。

$ go run mutexes.go
map[a:20000 b:10000]

接下來我們將著眼於使用僅有 goroutine 和通道來實作相同的狀態管理任務。

下一個範例:有狀態的 goroutine