前言: 在工作实际开发中,遇到这个问题;记录一下。
1. 先看问题代码
func main() {
wg:= errgroup.Group
pageIndex := 0
for i := 0; i < 5; i++ {
pageIndex++
wg.Go(func() error {
fmt.Println("pageIndex", pageIndex)
return nil
})
}
if err := wg.Wait(); err != nil {
fmt.Println("err")
}
fmt.Println("ok")
}
//打印结果
pageIndex: 5
pageIndex: 5
pageIndex: 5
pageIndex: 5
pageIndex: 5
ok
原因:
通过打印发现,pageIndex全部是5,因为wg.Go里面的pageIndex是外部变量(该变量内存地址一直没有发生改变);go协程里面是先得到pageIndex内存地址,然后根据地址取值;当for循环执行完毕,pageIndex此时完成累加(值为5);go协程再去执行打印pageIndex的时候就全都是5了。(有可能某个groutine执行的够快。会打印出不同的值)
根据代码解释会更清楚一些
func main() {
wg:= errgroup.Group
pageIndex := 0
for i := 0; i < 5; i++ {
pageIndex++
wg.Go(func() error {
//第一个groutine拿到pageIndex的地址:0xc666,虽然此时0x666的值为1;但是还没执行
//第二个groutine拿到pageIndex的地址:0xc666,虽然此时0x666的值为2;但是还没执行
//第三个groutine拿到pageIndex的地址:0xc666,虽然此时0x666的值为3;但是还没执行
//......
fmt.Println("pageIndex", pageIndex)
return nil
})
}
//最后拿到pageIndex的地址:0x666,此时0x666的值为5, groutine再执行打印语句,根据0x666得到结果为5
if err := wg.Wait(); err != nil {
fmt.Println("err")
}
fmt.Println("ok")
}
//打印结果
pageIndex: 5
pageIndex: 5
pageIndex: 5
pageIndex: 5
pageIndex: 5
ok
正确姿势
func main() {
wg:= errgroup.Group
//pageIndex := 0
for i := 0; i < 5; i++ {
//for循环内部创建变量
pageIndex := i + 1
wg.Go(func() error {
//第一次拿到pageIndex的地址:0xc661,虽然此时0x661的值为1;但是还没执行
//第二次拿到pageIndex的地址:0xc662,虽然此时0x662的值为2;但是还没执行
//第三次拿到pageIndex的地址:0xc663,虽然此时0x663的值为3;但是还没执行
//......
fmt.Println("pageIndex", pageIndex)
return nil
})
}
//groutine执行打印语句,根据不同的内存地址获得不同的值
if err := wg.Wait(); err != nil {
fmt.Println("err")
}
fmt.Println("ok")
}
//打印结果
pageIndex: 5
pageIndex: 2
pageIndex: 3
pageIndex: 4
pageIndex: 1
ok
如果在内部新建变量的话,每个pageIndex的内存地址都不一样,因此可以根据不同的内存地址拿到不同的值。