go協程全局變量和局部變量

原文鏈接:http://www.zhoubotong.site/post/19.html

大家可能經常會用到類似如下代碼片段:

package main

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

func main() {
   sli := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
   wg := sync.WaitGroup{}
   for k, v := range sli {
      wg.Add(1)
      go func() {
         time.Sleep(time.Second)
         fmt.Println(k, v)
         wg.Done()
      }()
   }
   wg.Wait()
}

打印輸出:

9 9
9 9
9 9
9 9
9 9
9 9
9 9
9 9
9 9
9 9

結果是不是和想象的不一樣?,主要原因出在協程這裏,如果不使用協程,直接使用串行的方式,結果結合預期一致,比如:

package main

import (
   "fmt"
   "time"
)

func main() {
   sli := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
   for k, v := range sli {
      func() {
         time.Sleep(time.Second)
         fmt.Println(k, v)
      }()
   }
}

打印輸出:

0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9

那爲什麼上面使用攜程的輸出都是相同值?我們來解讀下:
其中 k, v 是迭代變量,每次迭代都會給 k, v 賦值新的值,並且多個協程又同時調用了 k, v ,所以結果就串了,那攜程怎麼解決?解決方式我們可以定義一個局部變量。

package main

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

func main() {
   sli := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
   wg := sync.WaitGroup{}
   for k, v := range sli {
      wg.Add(1)
      k1 := k
      v1 := v
      go func() {
         time.Sleep(time.Second)
         fmt.Println(k1, v1)
         wg.Done()
      }()
   }
   wg.Wait()
}

k1, v1 是局部變量,每次循環,循環體內是不共享的,這也是爲什麼可以這樣聲明變量(k1 := k)。

或者通過傳參的方式來固定下來,比如像下面這樣:

package main

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

func main() {
   sli := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
   wg := sync.WaitGroup{}
   for k, v := range sli {
      wg.Add(1)

      go func(k, v interface{}) {
         time.Sleep(time.Second)
         fmt.Println(k, v)
         wg.Done()
      }(k, v)
   }
   wg.Wait()
}

這樣輸出就正常,比如輸出如下:

0 0
5 5
2 2
3 3
4 4
1 1
9 9
6 6
8 8
7 7

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章