如何理解goroutine的这种行为? [英] How to understand this behavior of goroutine?

查看:82
本文介绍了如何理解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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆