go 闭包问题

代码如下:

package main

import (
    "fmt"
    "sync"
    "time"
)

var a = [...]int{1, 2, 3, 4, 5}

func Run() {
    fmt.Println("begin")
    var wg sync.WaitGroup
    wg.Add(len(a))
    for _, i := range a {
        /*        go func(i int) { // 函数变量是以值的方式传递的
                fmt.Printf("get value %d \n", i)
                time.Sleep(time.Second)
                wg.Done()
            }(i)*/
        go func() {
            fmt.Printf("get value %d\n", i)
            time.Sleep(time.Second)
            wg.Done()
        }()
    }
    wg.Wait()
    fmt.Println("end")
}

func main() {
    Run()
}

运行结果输出:
begin
get value 5
get value 5
get value 5
get value 5
get value 5
end

不给匿名函数传参的话,为什么只能获取最后一个值,其中原理还是想不太明白,请各位大哥给讲解下,谢谢!!!

阅读 1.9k
3 个回答

你可以认为是调度问题

有两点需要注意:

  1. i 不是每次循环的局部变量,而是整个 for 块的全局变量,你的所有 routine 中的 i 引用的是同一个 i
  2. 新开 routine 是一个费时操作,而循环却非常快,所以还没有等到一个 routine 引用 i,for 循环就结束了,此时的 i 已经就是最后一个值了

这是golang很经典的问题。
当在循环中启动协程时,协程的启动一定不会跟循环同步。
你闭包执行的值是什么取决于协程启动时循环到的位置。
就像你的例子,当你的for循环完之后,你定义的go协程才启动执行,这个时候你的i值统一都是5.
如果你当初参数传入,那么这个值就是是定义闭包时,循环到的值了。

因为你的i在原本的循环里已经改变,闭包里的i并不是不变的。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题