Go语言for循环内goroutine仅获取切片最后值问题咨询
问题原因解析
1. 循环变量的复用机制
Go的for range循环中,每次迭代的v是同一个内存地址的变量,不会每次迭代都重新声明新变量、分配新内存,循环全程只会不断修改这个变量的值。
你代码里启动的goroutine没有把v作为参数传入,匿名函数作为闭包直接捕获了v变量本身,而不是捕获当前迭代时v的具体值,也就是说所有goroutine引用的都是同一个v的内存地址。
2. goroutine的调度延迟
调用go func()启动goroutine时,只是把这个goroutine提交给了Go的调度器等待调度,不会立刻执行。你示例里遍历5个元素的切片耗时极短,大概率所有goroutine还没被调度执行,循环就已经跑完了,此时循环变量v的值已经被更新为切片最后一个元素的值5。等到goroutine真正执行打印语句的时候,读取到的v自然全是5。
注意:并不是所有场景下goroutine都会等循环完全跑完才执行,如果循环内部有耗时操作,或者调度器刚好在迭代中间触发了goroutine调度,也可能出现打印出中间值的情况,不是必然全部输出最后一个值,只是你这个示例场景下遍历速度太快才会全输出
5。
修复方案
如果要让每个goroutine拿到当前迭代的v的值,只需要把v作为参数传入匿名函数即可,每次传参的时候都会对当前v的值做拷贝,每个goroutine拿到的就是独立的当前迭代值:
package main import ( "fmt" "time" ) func main() { a := []int64{1, 2, 3, 4, 5} for _,v := range a { // 把v作为参数传入匿名函数 go func(val int64){ fmt.Println("value is ", val) }(v) // 传参操作在启动goroutine前就会执行,会捕获当前迭代的v的值 } time.Sleep(2*time.Second) }
修改后运行就会输出1到5的所有值,输出顺序不固定,因为goroutine的调度顺序没有保证。
常见误区澄清
- 不是goroutine故意等循环跑完才执行,是循环执行速度远快于goroutine被调度的速度,才会出现这种现象
- 闭包捕获外部变量是捕获变量的引用,不是捕获值,这个特性不止Go有,很多支持闭包的语言都有类似的行为
内容的提问来源于stack exchange,提问作者Sruthi CP
相关产品推荐
相关产品推荐

