我们会经常性的使用如下片段
for k, v := range sli {
go ...
}
来看下面的例子
func main() {
sli := []int{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()
}
打印输出
8 9
8 9
8 9
8 9
8 9
8 9
8 9
8 9
8 9
结果和想象的不一样,主要原因出在协程这里,如果不使用协程,而是使用串行的方式,结果结合预期一致,比如
func main() {
sli := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
for k, v := range sli {
func() {
time.Sleep(time.Second)
fmt.Println(k, v)
}()
}
}
打印输出
0 1
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
其中 k, v 是迭代变量,每次迭代都会给 k, v 赋值新的值,并且多个协程又同时调用了 k, v ,所以结果就串了,解决方式就是定义一个局部变量。
func main() {
sli := []int{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()
}
打印输出
4 5
1 2
2 3
8 9
7 8
5 6
6 7
0 1
3 4
k1, v1 是局部变量k1 := k
或者通过传参的方式来固定下来
func main() {
sli := []int{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()
}