Golang - 教學 tutorial

GoLang - 多線程全局變數加鎖

在這幾篇,會以 Go 語言的入門基礎進行逐步說明,本篇針對 多線程全局變數加鎖 進行說明

在 go 使用多線程時,因為 goroutine 提供了共用記憶體資料的機制,如果操作同一個變數,會引發資源競爭的情況。

在正常情況,goroutine 透過 channel 來通訊會比較恰當。

在這裡則是說明,如何在 goroutine 處理操作 global 變數,如何避免資源競爭的情況。

首先,先了解在資源競爭的情況,在 goroutine 運行期間,同時對 globalMap 進行資料讀寫的情況,例如:

package main

import (
  "fmt"
  "time"
)

var (
  globalMap = make(map[int]int)
)

func syncProcess(num int) {
  result := 1
  for i := 0; i < num; i++ {
    result *= i
  }
  globalMap[num] = result

}

func main() {
  for i := 0; i < 100; i++ {
    go syncProcess(i)
  }
  //sleep 10 seconds
  time.Sleep(time.Second * 10)
  fmt.Println(globalMap)
}

運行時,程式會出現 concurrent map writes 的錯誤訊息,如下:

fatal error: concurrent map writes
fatal error: concurrent map writes

goroutine 4 [running]:
runtime.throw(0x10c90fd, 0x15)
	/usr/local/go/src/runtime/panic.go:617 +0x72 fp=0xc000034760 sp=0xc000034730 pc=0x1028192
runtime.mapassign_fast64(0x10ab140, 0xc000062180, 0x0, 0x100000000000000)
	/usr/local/go/src/runtime/map_fast64.go:101 +0x35f fp=0xc0000347a0 sp=0xc000034760 pc=0x100efff
main.syncProcess(0x0)
	/Users/liaoxiangru/docker_project/go/main.go:17 +0x61 fp=0xc0000347d8 sp=0xc0000347a0 pc=0x1093ac1
runtime.goexit()
	/usr/local/go/src/runtime/asm_amd64.s:1337 +0x1 fp=0xc0000347e0 sp=0xc0000347d8 pc=0x1051b01

sync.Mutex 加鎖

要解決這情況,可以透過 sync 來處理全局變數加鎖的方式來處理,

sync 預設包含了幾個方法:

  • sync 同步機制(synchornized)
  • Mutex 互斥機制 (包含 Lock() 加鎖, Unlock() 解鎖)

在這裡加上 sync.Mutex聲明全局的互斥鎖

package main

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

var (
  globalMap = make(map[int]int)
  lock      sync.Mutex
)

func syncProcess(num int) {
  result := 1
  for i := 0; i < num; i++ {
    result *= i
  }
  lock.Lock()
  globalMap[num] = result
  lock.Unlock()

}

func main() {
  for i := 0; i < 100; i++ {
    go syncProcess(i)
  }
  //sleep 10 seconds
  time.Sleep(time.Second * 10)
  fmt.Println(globalMap)
}

輸出結果

map[0:1 1:0 2:0 3:0 4:0 5:0 6:0 7:0 8:0 9:0 10:0 11:0 12:0 13:0 14:0 15:0 16:0 17:0 18:0 19:0 20:0 21:0 22:0 23:0 24:0 25:0 26:0 27:0 28:0 29:0 30:0 31:0 32:0 33:0 34:0 35:0 36:0 37:0 38:0 39:0 40:0 41:0 42:0 43:0 44:0 45:0 46:0 47:0 48:0 49:0 50:0 51:0 52:0 53:0 54:0 55:0 56:0 57:0 58:0 59:0 60:0 61:0 62:0 63:0 64:0 65:0 66:0 67:0 68:0 69:0 70:0 71:0 72:0 73:0 74:0 75:0 76:0 77:0 78:0 79:0 80:0 81:0 82:0 83:0 84:0 85:0 86:0 87:0 88:0 89:0 90:0 91:0 92:0 93:0 94:0 95:0 96:0 97:0 98:0 99:0]