go中有循环和goroutinues的意外行为

时间:2013-08-11 05:51:32

标签: for-loop concurrency go goroutine

为什么这样:

for i := 0; i < 3; i++ {
    go func(i int) {
        fmt.Printf("%d", i)
    }(i)
}

打印012

虽然:

for i := 0; i < 3; i++ {
    go func() {
        fmt.Printf("%d", i)
    }()
}

打印333?

2 个答案:

答案 0 :(得分:3)

虽然goroutines便宜,但它们不是免费的。创建它们有一些但很少的开销。

在你的第一个程序中,i的值保留在goroutines中,因为你将它作为参数传递。 (此时每个goroutine都会获得自己i值的副本。)

在第二个程序中,i的值在第一个goroutine开始之前已经是3。请记住,goroutine在Go程序中共享相同的内存空间,因此在这种情况下,每个goroutine在打印出来时都会查看相同的i

答案 1 :(得分:2)

for循环之后添加一个print语句应该会让你明白。您将看到该print语句在您的goroutine函数之前运行。

如果你在for循环中所做的只是启动一个新的goroutine,你的循环通过非常快,通常在你的第一个goroutine开始之前完成。因此,当您的goroutine开始循环时,您的循环已经完成,if i的值为3。记住这一点。

当您将i作为函数参数传递时,就像在第一个示例中那样,它的当前值是复制到函数堆栈,因此函数接收其当前值为一个论点。这就是为什么你会看到012。但是当一个闭包函数只是在它的周围范围内使用一个变量时,就像你在第二个例子中那样,它在运行时访问它的当前值,在你的情况下是在循环结束并且i已经到达之后3

您可以使用以下代码查看此效果:

for i := 0; i < 3; i++ {
    go func(arg int) {
        fmt.Printf("%d %d\n", arg, i)
    }(i)
}

产生这个输出:

0 3
1 3
2 3