如何将方法用作 goroutine 函数
How to use a method as a goroutine function
我有这个代码。我希望输出:
hello : 1
world : 2
但它输出:
world : 2
world : 2
我的代码有问题吗?
package main
import (
"fmt"
"time"
)
type Task struct {
name string
data int32
}
func (this *Task) PrintData() {
fmt.Println(this.name, ":", this.data)
}
func main() {
tasks := []Task{{"hello", 1}, {"world", 2}}
for _, task := range tasks {
go task.PrintData()
}
time.Sleep(time.Second * 5000)
}
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 help detect this and other
problems before they happen, run go vet.
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
}()
}
只需使用看似奇怪但在 Go 中工作正常的声明样式为闭包创建一个新变量。添加task := task
。例如,
package main
import (
"fmt"
"time"
)
type Task struct {
name string
data int32
}
func (this *Task) PrintData() {
fmt.Println(this.name, ":", this.data)
}
func main() {
tasks := []Task{{"hello", 1}, {"world", 2}}
for _, task := range tasks {
task := task
go task.PrintData()
}
time.Sleep(time.Second * 5000)
}
输出:
hello : 1
world : 2
因为PrintData
是一个指针接收者而task
是一个值,编译器在进行方法调用时会自动取task
的地址。结果调用与 (&task).PrintData()
.
相同
变量 task
在循环的每次迭代中设置为不同的值。第一个 goroutine 不会 运行 直到 task
被设置为第二个值。 运行 this example 以查看每次迭代都将相同的地址传递给 PrintData。
有几种方法可以解决这个问题。第一种是在切片中使用*Task
:
tasks := []*Task{{"hello", 1}, {"world", 2}}
for _, task := range tasks {
go task.PrintData()
}
第二种是在循环内创建一个新变量:
tasks := []Task{{"hello", 1}, {"world", 2}}
for _, task := range tasks {
task := task
go task.PrintData()
}
第三种是取slice元素的地址(使用自动插入地址操作):
tasks := []Task{{"hello", 1}, {"world", 2}}
for i := range tasks {
go tasks[i].PrintData()
}
另一种选择是将 PrintData 更改为值接收器,以防止方法调用自动获取 task
:
的地址
func (this Task) PrintData() {
fmt.Println(this.name, ":", this.data)
}
这个问题类似于 issue discussed in the closures and goroutines FAQ. The difference between the issues is the mechanism used to pass a pointer to the goroutine function. The code in the question uses the method's receiver argument. The code in the FAQ uses a closure。
我有这个代码。我希望输出:
hello : 1
world : 2
但它输出:
world : 2
world : 2
我的代码有问题吗?
package main
import (
"fmt"
"time"
)
type Task struct {
name string
data int32
}
func (this *Task) PrintData() {
fmt.Println(this.name, ":", this.data)
}
func main() {
tasks := []Task{{"hello", 1}, {"world", 2}}
for _, task := range tasks {
go task.PrintData()
}
time.Sleep(time.Second * 5000)
}
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 help detect this and other problems before they happen, run go vet.
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 }() }
只需使用看似奇怪但在 Go 中工作正常的声明样式为闭包创建一个新变量。添加task := task
。例如,
package main
import (
"fmt"
"time"
)
type Task struct {
name string
data int32
}
func (this *Task) PrintData() {
fmt.Println(this.name, ":", this.data)
}
func main() {
tasks := []Task{{"hello", 1}, {"world", 2}}
for _, task := range tasks {
task := task
go task.PrintData()
}
time.Sleep(time.Second * 5000)
}
输出:
hello : 1
world : 2
因为PrintData
是一个指针接收者而task
是一个值,编译器在进行方法调用时会自动取task
的地址。结果调用与 (&task).PrintData()
.
变量 task
在循环的每次迭代中设置为不同的值。第一个 goroutine 不会 运行 直到 task
被设置为第二个值。 运行 this example 以查看每次迭代都将相同的地址传递给 PrintData。
有几种方法可以解决这个问题。第一种是在切片中使用*Task
:
tasks := []*Task{{"hello", 1}, {"world", 2}}
for _, task := range tasks {
go task.PrintData()
}
第二种是在循环内创建一个新变量:
tasks := []Task{{"hello", 1}, {"world", 2}}
for _, task := range tasks {
task := task
go task.PrintData()
}
第三种是取slice元素的地址(使用自动插入地址操作):
tasks := []Task{{"hello", 1}, {"world", 2}}
for i := range tasks {
go tasks[i].PrintData()
}
另一种选择是将 PrintData 更改为值接收器,以防止方法调用自动获取 task
:
func (this Task) PrintData() {
fmt.Println(this.name, ":", this.data)
}
这个问题类似于 issue discussed in the closures and goroutines FAQ. The difference between the issues is the mechanism used to pass a pointer to the goroutine function. The code in the question uses the method's receiver argument. The code in the FAQ uses a closure。