一尘不染

为什么Golang在goroutines中对闭包的处理方式不同?

go

考虑以下Golang代码(也在Go Playground上):

package main

import "fmt"
import "time"

func main() {
    for _, s := range []string{"foo", "bar"} {
        x := s
        func() {
            fmt.Printf("s: %s\n", s)
            fmt.Printf("x: %s\n", x)
        }()
    }
    fmt.Println()
    for _, s := range []string{"foo", "bar"} {
        x := s
        go func() {
            fmt.Printf("s: %s\n", s)
            fmt.Printf("x: %s\n", x)
        }()
    }
    time.Sleep(time.Second)
}

此代码产生以下输出:

s: foo
x: foo
s: bar
x: bar

s: bar
x: foo
s: bar
x: bar

假设这不是一些奇怪的编译器错误,我很好奇为什么a)s的值在goroutine版本中的解释与常规func调用中的不同,并且b)为什么将其分配给循环内的局部变量起作用?两种情况。


阅读 228

收藏
2020-07-02

共1个答案

一尘不染

Go中的闭包在词法范围内。这意味着闭包内从“外部”范围引用的任何变量都不是副本,而是实际上是引用。一for环竟重复使用相同的变量多次,所以你介绍的读/写之间的竞争条件s变量。

但是x正在分配一个新变量(带有:=)并进行复制s,这导致每次都是正确的结果。

通常,最佳做法是传递所需的任何参数,以使您没有引用。例:

for _, s := range []string{"foo", "bar"} {
    x := s
    go func(s string) {
        fmt.Printf("s: %s\n", s)
        fmt.Printf("x: %s\n", x)
    }(s)
}
2020-07-02