在Go语言中,循环和goroutine可能会出现意外行为。

huangapple go评论69阅读模式
英文:

unexpected behavior with loops and goroutinues in go

问题

为什么这段代码:

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?

英文:

Why does this:

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

prints 012

while this:

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

prints 333?

答案1

得分: 3

虽然 goroutine 是廉价的,但它们并非免费。创建它们会有一些开销,尽管很小。

在你的第一个程序中,i 的值被保留在 goroutine 中,因为你将其作为参数传递进去。(每个 goroutine 在那一刻都会得到 i 值的自己的副本。)

在你的第二个程序中,第一个 goroutine 开始之前,i 的值已经是 3 了。请记住,在 Go 程序中,goroutine 共享同一块内存空间,所以在这种情况下,每个 goroutine 在打印时都看到的是同一个 i 的值。

英文:

While goroutines are cheap, they aren't free. There is some, but little, overhead in creating them.

In your first program, the value of i is preserved into the goroutines because you're passing it in as an argument. (Each goroutine gets its own copy of i's value at that moment.)

In your second program, the value of i is already 3 before the first goroutine has started. Remember that goroutines share the same memory space in a Go program, so in this case, each goroutine is looking at the same i when it prints it out.

答案2

得分: 2

在你的for循环之后添加一个打印语句,这样可以让事情变得清晰。你会发现,该打印语句在你的goroutine函数之前运行。

当你在for循环中只是启动一个新的goroutine时,你的循环会非常快速地执行,并且通常会在第一个goroutine开始之前就完成。因此,当你的goroutine开始时,你的循环已经完成,并且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
英文:

Adding a print statement just after your for loop should makes things clear for you. You’ll see that that print statement runs before your goroutine function.

When all you do in a for loop is to launch a new goroutine, your loop passes very fast and usually finishes even before your first goroutine starts. So when your goroutines start your loop is already finished and the value of if i is 3. Keep that in mind.

When you pass the i as a function argument, like you do in your first example, it’s current value is copied to the function stack, so the functions receives its current value as an argument. That’s why you’ll see 012. But when a closure function just uses a variable in its surrounding scope, like you do in your second example, it accesses its current value when it is run, which in your case is after the loop has finished and i has reached 3.

You can see this effect with this code:

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

which produces this output:

0 3
1 3
2 3

huangapple
  • 本文由 发表于 2013年8月11日 13:51:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/18169176.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定