網頁

2022/4/29

Golang Mutex互斥鎖簡介

Go goroutine做並行程序時,利用sync.Mutex來限制只能有一個goroutine存取程式區塊。


範例環境:

  • Go 1.18


未加Mutex鎖

下面主程式中發起了兩個goroutine執行inc()函式來累加package level變數count的值並印出goroutine名稱和count值。最後用time.Sleep()暫停避免提前結束。

main.go

package main

import (
    "fmt"
    "time"
)

var count int

func main() {
    go inc("a")
    go inc("b")

    time.Sleep(time.Second) // prevent main goroutine end first
}

func inc(name string) {
    count++
    fmt.Printf("%s:%d\n", name, count)
}

由於goroutine是不保證順序的並行程序,所以可能印出以下結果。

結果一

b:2
a:1

執行順序可能為:

  1. a count++
  2. a print count (hold)
  3. b count++
  4. b print count
  5. a print count

結果二

b:2
a:2

執行順序可能為:

  1. a count++
  2. b count++
  3. b print count
  4. a print count

從以上結果可觀察到goroutine執行inc()累加count和印出的結果不同,則就是因為外部變數count同時被多個goroutine存取且執行順序不定所導致。


加Mutex鎖

若希望goroutine執行inc()累加count和印出的值一致,則可用sync.Mutex.Lock()sync.Mutex.Unlock()限制一段區塊只能有一個goroutine存取,待該goroutine離開後才能允許下一個goroutine存取。

下面在inc()函式的前後加上sync.Mutex.Lock()sync.Mutex.Unlock()限制兩著間的程式區塊只能有一個goroutine存取,所以count++和印出的程式都只能由一個goroutine存取以此確保累加count後和印出的值一致。

main.go

package main

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

var count int

var mu sync.Mutex

func main() {
    go inc("a")
    go inc("b")

    time.Sleep(time.Second)
}

func inc(name string) {
    mu.Lock()
    count++
    fmt.Printf("%s:%d\n", name, count)
    mu.Unlock()
}

github


執行後可能印出以下結果。不論哪個goroutine先執行,count累加和印出同時只能由一個goroutine存取。

結果ㄧ

goroutine a先執行inc()

a:1
b:2

執行順序為:

  1. a count++
  2. a print count
  3. b count++
  4. b print count

結果二

goroutine b先執行inc()

b:1
a:2

執行順序為:

  1. b count++
  2. b print count
  3. a count++
  4. a print count


Go Mutex的LockUnlock類似Java synchronized的概念。


沒有留言:

張貼留言