如何理解goroutine的这种行为? [英] How to understand this behavior of goroutine?
问题描述
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.After(1 * time.Second)
}
为什么此代码按任意顺序打印3个三个"而不是一个"两个"三个"?
why does this code print 3 "three" instead of "one" "two" "three" in any order?
推荐答案
存在数据争用.
当评估goroutine函数的参数时,代码隐式地获取变量v
的地址 .请注意,呼叫v.print()
是呼叫(&v).print()
的简写.
The code implicitly takes address of variable v
when evaluating arguments to the goroutine function. Note that the call v.print()
is shorthand for the call (&v).print()
.
循环会更改变量v
的值.
执行goroutines时,会发生v
具有循环的最后一个值的情况.不能保证.它可以按预期执行.
When goroutines execute, it so happens that v
has the last value of the loop. That's not guaranteed. It could execute as you expected.
使用种族检测器可以轻松,轻松地运行程序.该数据争用由检测器检测并报告.
It's helpful and easy to run programs with the race detector. This data race is detected and reported by the detector.
一种解决方法是创建另一个变量,范围位于循环内部:
One fix is to create another variable scoped to the inside of the loop:
for _, v := range data {
v := v // short variable declaration of new variable `v`.
go v.print()
}
通过此更改,在评估goroutine的参数时将使用内部变量v
的地址.循环的每次迭代都有一个唯一的内部变量v
.
With this change, the address of the inner variable v
is taken when evaluating the arguments to the goroutine. There is a unique inner variable v
for each iteration of the loop.
解决该问题的另一种方法是使用切片指针:
Yet another way to fix the problem is use a slice of pointers:
data := []*field{ {"one"},{"two"},{"three"} } // note '*'
for _, v := range data {
go v.print()
}
进行此更改后,切片中的各个指针将传递给goroutine,而不是范围变量v
的地址.
With this change, the individual pointers in the slice are passed to the goroutine, not the address of the range variable v
.
另一种解决方法是使用slice元素的地址:
Another fix is to use the address of the slice element:
data := []field{ {"one"},{"two"},{"three"} } // note '*'
for i:= range data {
v := &data[i]
go v.print()
}
由于指针值通常与具有指针接收器的类型一起使用,因此在实践中通常不会出现这种细微问题.由于field
具有指针接收器,因此对于问题中的data
类型,通常使用[]*field
而不是[]field
.
Because pointer values are typically used with types having a pointer receiver, this subtle issue does not come up often in practice. Because field
has a pointer receiver, it would be typical to use []*field
instead of []field
for the type of data
in the question.
如果goroutine函数位于匿名函数中,那么避免该问题的常见方法是将范围变量作为参数传递给匿名函数:
If the goroutine function is in an anonymous function, then a common approach for avoiding the issue is to pass the range variables as an argument to the anonymous function:
for _, v := range data {
go func(v field) {
v.print() // take address of argument v, not range variable v.
}(v)
}
由于问题中的代码尚未对goroutine使用匿名函数,因此此答案中使用的第一种方法更为简单.
Because the code in the question does not already use an anonymous function for the goroutine, the first approach used in this answer is simpler.
这篇关于如何理解goroutine的这种行为?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!