golang協程閉包數據陷阱

0x1

我們在寫協程程序的時候,經常會碰到一個場景就是我們要分發執行任務給不同的goroutine(簡稱gor),然后再把各個gor的處理結果匯總起來,這個時候就要注意gor的數據污染問題,我們可以通過閉包來防范各個gor之間的數據污染

0x2

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


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循環的值

注釋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,來進行內存數據作用域鎖定,閉包的原理,防止數據污染

所以運行的結果如我們所想

本文鏈接:參與評論 ?

--EOF--

專題「編程語言」的其它文章 ?

Comments

toto足球指数