为什么在Go协程中,匿名函数的局部变量和参数不同?

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

why local variable is different parameter at anonymous function in goroutine

问题

你好!以下是你提供的代码的中文翻译:

package main

import (
	"fmt"
	"runtime"
)

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	fmt.Println(runtime.GOMAXPROCS(0))

	// s := "hello world \n"

	for i := 0; i < 100; i++ {
		go func(n int) {
			fmt.Println(n, i)
		}(i)
	}

	fmt.Scanln()
}

我只提供代码的翻译,不回答关于翻译的问题。

英文:
package main

import (
  &quot;fmt&quot;
  &quot;runtime&quot;
)

func main() {
  runtime.GOMAXPROCS(runtime.NumCPU())
  fmt.Println(runtime.GOMAXPROCS(0))

  // s := &quot;hello world \n&quot;

  for i := 0; i &lt; 100; i++ {
	go func(n int) {
		fmt.Println(n, i)
	}(i)
  }

  fmt.Scanln()
}

I just wondering why n is not equal to i every go routine.

Also i sometimes has the same value as in the previous call.

What is the matter in this code?

答案1

得分: 9

这个主题已经有很多人讨论过(涉及多种语言),但简单来说就是这样的:

你当前的输出是这样的:

1 100
2 100
3 100
4 100
...

变量 i 成为闭包的一部分。这意味着它的值实际上超出了它的作用域(它被移动到一边,以便当 goroutine 执行时知道在哪里找到 i)。

当调度器开始执行你的 goroutine 时,for 循环已经结束,i 的值将会是 100(如果你在一台慢机器上运行,可能会接近 100)。

修复的方法是在每次迭代中存储该值并使用它:

for i := 0; i < 100; i++ {
    x := i            // <------ 在每次循环迭代中存储 i 的值
    go func(n int) {
        fmt.Println(n, x) // <---- 使用新的、局部的、闭包的值,而不是主闭包的值
    }(i)
} 

现在每个 goroutine 引用自己的闭包变量 x 的副本,输出变为:

1 1
2 2
3 3
4 4
...

这种现象不仅仅局限于 Go 语言。

你可以在 Playground 上看到一个可运行的示例:
在 Playground 上查看

英文:

This topic is well covered (across multiple languages) - but the short version is this:

Your current output is like this:

1 100
2 100
3 100
4 100
...

The variable i becomes part of a closure. This means that its value actually outlasts its scope (its moved off to the side so that when the goroutine executes it knows where to find i).

By the time the scheduler gets around to executing your goroutines, the for loop has finished and the value of i is going to be 100 (or close to it if you're running on a slow machine).

The fix is to store the value every iteration and use that:

for i := 0; i &lt; 100; i++ {
    x := i            // &lt;------ Store the value of i each loop iteration
    go func(n int) {
        fmt.Println(n, x) // &lt;---- Use the new, local, closed over value, not the main closed over one
    }(i)
} 

Now every goroutine references its own copy of a closed over variable x, and your output becomes:

1 1
2 2
3 3
4 4
...

This phenomenon is not isolated to Go.

You can see a working sample on the playground:
<kbd>View it on the Playground</kbd>

huangapple
  • 本文由 发表于 2015年12月4日 10:42:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/34080050.html
匿名

发表评论

匿名网友

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

确定