原文鏈接: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