代码如下:
type field struct {
name string
}
func print(p *field) {
fmt.Println(p.name)
}
func fix1() {
data := []*field{{name: "one"}, {name: "two"}, {name: "three"}}
for _, v := range data {
go print(v)
}
time.Sleep(time.Millisecond * 200)
}
func wrong1() {
data := []*field{{name: "one"}, {name: "two"}, {name: "three"}}
for _, v := range data {
go func() {
print(v)
}()
}
time.Sleep(time.Millisecond * 200)
}
func main() {
wrong1()
}
据我所知,函数 wrong1
中的所有 goroutine 共享同一个局部变量 v
。在goroutine执行的那一刻,v
的值可能等于data
中的任意值,因此函数会打印3次随机数据。
但是,我无法理解为什么函数 fix1
的行为不同(它将 data
中的每个值打印一次)。
最佳答案
wrong1(): go func() { print(v) }()
Go: Frequently Asked Questions (FAQ)
What happens with closures running as goroutines?
Some confusion may arise when using closures with concurrency. Consider the following program:
func main() { done := make(chan bool) values := []string{"a", "b", "c"} for _, v := range values { go func() { fmt.Println(v) done <- true }() } // wait for all goroutines to complete before exiting for _ = range values { <-done } }
One might mistakenly expect to see a, b, c as the output. What you'll probably see instead is c, c, c. This is because each iteration of the loop uses the same instance of the variable v, so each closure shares that single variable. When the closure runs, it prints the value of v at the time fmt.Println is executed, but v may have been modified since the goroutine was launched.
To bind the current value of v to each closure as it is launched, one must modify the inner loop to create a new variable each iteration. One way is to pass the variable as an argument to the closure:
for _, v := range values { go func(u string) { fmt.Println(u) done <- true }(v) }
In this example, the value of v is passed as an argument to the anonymous function. That value is then accessible inside the function as the variable u.
Even easier is just to create a new variable, using a declaration style that may seem odd but works fine in Go:
for _, v := range values { v := v // create a new 'v'. go func() { fmt.Println(v) done <- true }() }
你的 wrong1
例子,
for _, v := range data {
go func() {
print(v)
}()
}
Playground :https://play.golang.org/p/0w86nvVMt1g
输出:
three
three
three
你的 wrong1
示例,创建一个新变量,
for _, v := range data {
v := v
go func() {
print(v)
}()
}
Playground :https://play.golang.org/p/z5RCI0ZZU8Z
输出:
one
two
three
你的错误 1
示例,将变量作为参数传递,
for _, v := range data {
go func(v *field) {
print(v)
}(v)
}
Playground :https://play.golang.org/p/1JVI7XYSqvv
输出:
one
two
three
fix1(): go print(v)
The Go Programming Language Specification
Given an expression f of function type F,
f(a1, a2, … an)
calls f with arguments a1, a2, … an. Except for one special case, arguments must be single-valued expressions assignable to the parameter types of F and are evaluated before the function is called.
The function value and parameters are evaluated as usual in the calling goroutine.
您的 fix1
示例,在调用函数之前评估 v
的值,
for _, v := range data {
go print(v)
}
Playground :https://play.golang.org/p/rN3UNaGi-ge
输出:
one
two
three
关于pointers - `go print(v)` 和 `go func() { print(v) }()` 之间的区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53987867/