这个主题很好地涵盖了(跨多种语言) - 但简短的版本是这样的:
您当前的输出是这样的:
1 100 2 100 3 100 4 100 ...
变量i
成为闭包的一部分.这意味着它的值实际上超出了它的范围(它移到了一边,以便当goroutine执行时它知道在哪里找到i
).
当调度程序开始执行你的goroutine时,for
循环已经完成并且值i
将为100(或者如果你在慢速机器上运行则接近它).
修复是每次迭代存储值并使用:
for i := 0; i < 100; i++ { x := i // <------ Store the value of i each loop iteration go func(n int) { fmt.Println(n, x) // <---- Use the new, local, closed over value, not the main closed over one }(i) }
现在每个goroutine引用它自己的一个闭合变量的副本x
,你的输出变为:
1 1 2 2 3 3 4 4 ...
这种现象并不孤立于Go.
你可以在操场上看到一个工作样本: View it on the Playground
这个主题很好地涵盖了(跨多种语言) - 但简短的版本是这样的:
您当前的输出是这样的:
1 100 2 100 3 100 4 100 ...
变量i
成为闭包的一部分.这意味着它的值实际上超出了它的范围(它移到了一边,以便当goroutine执行时它知道在哪里找到i
).
当调度程序开始执行你的goroutine时,for
循环已经完成并且值i
将为100(或者如果你在慢速机器上运行则接近它).
修复是每次迭代存储值并使用:
for i := 0; i < 100; i++ { x := i // <------ Store the value of i each loop iteration go func(n int) { fmt.Println(n, x) // <---- Use the new, local, closed over value, not the main closed over one }(i) }
现在每个goroutine引用它自己的一个闭合变量的副本x
,你的输出变为:
1 1 2 2 3 3 4 4 ...
这种现象并不孤立于Go.
你可以在操场上看到一个工作样本: View it on the Playground