You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 06:54:07