背景
我在看50种颜色的围棋,特别是
Iteration Variables and Closures in "for" Statements
,我要从中摘录一段。
错误的
package main
import (
"fmt"
"time"
)
type field struct {
name string
}
func (p *field) print() {
fmt.Println(p.name)
}
func main() {
data := []field{{"one"},{"two"},{"three"}}
for _,v := range data {
go v.print()
}
time.Sleep(3 * time.Second)
//goroutines print: three, three, three
}
对的
变化
[]field{{"one"},{"two"},{"three"}}
到
[]*field{{"one"},{"two"},{"three"}}
和
one
,
two
和
three
将按顺序打印。
我的思维过程
-
在不正确的情况下,
go v.print()
被替换为
go (&v).print()
由编译器执行,因为
print()
在指针接收器上定义。
-
在生成的goroutine被执行之前,运行时只知道goroutine应该执行
打印()
,但不知道应将哪个实例作为接收器传递的指针。
-
当派生的goroutine被执行时,for循环很可能已经终止,所以当我们想决定哪个值应该作为接收方传递时,我们得到
v
,它在整个循环中共享,并在每次迭代中更新,因此我们传递
data
作为接受者
打印()
,这就是为什么我们得到3
三
印刷的。
问题
对我来说,改变
[]field
到
[]*field
只允许编译器跳过步骤1,但不更改步骤2和步骤3,所以我不知道为什么这样可以解决问题。
我想我的思维过程中一定有一些缺陷,我很感激你的建议。
更新
我碰巧看到另一个正确的实现
here
,我想我可能知道我的思维过程中哪里出错了。
data := []field{{"one"}, {"two"}, {"three"}}
for i := range data {
go data[i].print()
}
问题是,要传递给
打印()
作为接收器,在步骤2而不是步骤3中确定。这意味着在不正确的版本中,我们在每次迭代中传递相同的地址,但是它指向的内容(
数据
)在每次迭代中更新。但是,在正确的版本中,指针被传递到
打印()
作为接收器,指向
field
. 这同样适用于使用索引的情况。