作为goroutines运行的闭包发生了什么?|Go主题月

1,501 阅读1分钟

Go语言的协程——Goroutine。进程(Process),线程(Thread),协程(Coroutine,也叫轻量级线程)

当使用并发闭包时,可能会引起一些混乱。 看一下以下程序:

func main() {
    done := make(chan bool)

    values := []string{"a", "b", "c"}
    for _, v := range values {
        go func() {
            fmt.Println(v)
            done <- true
        }()
    }

    // wait for all goroutines to complete before exiting
    for _ = range values {
        <-done
    }
}

人们可能会错误地认为a、b、c是输出。但实际上可能会看到的会是c, c, c。这是因为循环的每次迭代都使用变量v的相同实例,所以每个闭包共享这个单一变量。当闭包运行时,它会在执行fmt.Println时打印v的值,但v可能在goroutine启动后被修改过。为了在这些问题发生之前帮助检测它们,可以运行go vet。

要在启动每个闭包时将v的当前值绑定到它,必须修改内部循环以在每次迭代时创建一个新的变量。一种方法是将变量作为参数传递给闭包:

    for _, v := range values {
        go func(u string) {
            fmt.Println(u)
            done <- true
        }(v)
    }

在这个例子中,v的值作为参数传递给匿名函数。这个值可以在函数内部作为变量u访问。 更简单的方法是创建一个新的变量,使用一种看似奇怪但在Go中运行良好的声明样式:


    for _, v := range values {
        v := v // create a new 'v'.
        go func() {
            fmt.Println(v)
            done <- true
        }()
    }

这种奇怪的行为,没有为每次迭代定义一个新的变量,现在看来,它的出现可能是一个错误。它可能在以后的版本中解决,但为了兼容性,不能在Go 1中更改。

外文翻译计划golang.org/doc/faq#clo…