golang協程閉包數據陷阱

0x1

咱們在寫協程程序的時候,常常會碰到一個場景就是咱們要分發執行任務給不一樣的goroutine(簡稱gor),而後再把各個gor的處理結果彙總起來,這個時候就要注意gor的數據污染問題,咱們能夠經過閉包來防範各個gor之間的數據污染golang

0x2

下面的一個gor之間數據互相污染的範例shell

func main() {
  setMem := make(map[int]int)
  wg := sync.WaitGroup{}
  lk := sync.RWMutex{}

  for i := 0; i < 10; i++ {    // 1
    wg.Add(1)
    
    go func(wg *sync.WaitGroup) {    // 2
      fmt.Println("i ---- ", i)
      defer wg.Done()
      lk.Lock()
      setMem[i] = i * 2
      lk.Unlock()
    }(&wg)
    
  }

  wg.Wait()
  fmt.Println("setMem 長度爲 ----------", len(setMem))
  fmt.Println("setMem done ... ")
}

這個的運行結果並不是如咱們所想,彙總了10個 gor的處理結果到 setMem 這個map中去安全

setMem 長度爲 ---------- 1
setMem done ...

緣由分析閉包

註釋1 由於main gor 和 go func 是同時在運行的, 因此main在賦值給 i 的同時, 2 接收到 i 的值並無鎖定 i , 而是接收到最新的for循環的值code

註釋2 沒有一個安全的內存區域儲存傳給當前 gor 的 i 值,因此形成數據污染協程

0x3

用閉包的原理去解決這個問題內存

func setM(wg *sync.WaitGroup, lk *sync.RWMutex, setMemx *map[int]int, i int) {
  defer wg.Done()
  fmt.Println("i ---- ", i)
  (*setMemx)[i] = i * 2
}

func main() {
  setMemx := make(map[int]int)
  wg := sync.WaitGroup{}
  lk := sync.RWMutex{}
   
  for i := 0; i < 10; i++ {  		 // 1
    wg.Add(1)
    setM(&wg, &lk, &setMemx, i)      // 2
  }

  wg.Wait()
  fmt.Println("setMem 長度爲 ----------", len(setMemx))
  fmt.Println("setMem done ... ")
}

運行結果作用域

setMen 長度爲 ---------- 10
setMem done ...

註釋1 1 處依然是經過循環賦予 i 值 註釋2 2經過聲明獨立的func,來進行內存數據做用域鎖定,閉包的原理,防止數據污染it

因此運行的結果如咱們所想for循環

相關文章
相關標籤/搜索