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