我正在尝试使用 channel 来实现一种工作池。请看下面的代码
https://play.golang.org/p/g7aKxDoP9lf (围棋 Playground )
package main
import (
"fmt"
"time"
)
func main() {
q1 := make(chan int)
fmt.Printf("worker 1\n")
go worker1(q1)
for i := 0; i < 10; i++ {
fmt.Printf("sending: %v\n", i)
q1 <- i
}
time.Sleep(time.Second)
fmt.Printf("\n\nworker 2\n")
q2 := make(chan *int)
go worker2(q2)
for i := 0; i < 10; i++ {
fmt.Printf("sending: %v\n", i)
q2 <- &i
}
time.Sleep(time.Second)
}
func worker1(qTodo <-chan int) {
var curr int
for {
select {
case curr = <-qTodo:
fmt.Printf("got: %v\n", curr)
}
}
}
func worker2(qTodo <-chan *int) {
var curr *int
for {
select {
case curr = <-qTodo:
fmt.Printf("got: %v\n", *curr)
}
}
}
这是一个示例输出
worker 1
sending: 0
got: 0
sending: 1
sending: 2
got: 1
got: 2
sending: 3
sending: 4
got: 3
got: 4
sending: 5
sending: 6
got: 5
got: 6
sending: 7
sending: 8
got: 7
got: 8
sending: 9
got: 9
worker 2
sending: 0
got: 0
sending: 1
sending: 2
got: 2
got: 2
sending: 3
sending: 4
got: 4
got: 4
sending: 5
sending: 6
got: 6
got: 6
sending: 7
sending: 8
got: 8
got: 8
sending: 9
got: 10
似乎在 worker2 收到指针时,原始变量中的值已经改变,这反射(reflect)在打印的值中。
问题是如何避免这种情况?如何解决这个问题?
最佳答案
此问题在 the Channels section of Effective Go 中有介绍.这是一个简短的摘录,其中变量名称已更改以匹配您的代码:
The bug is that in a Go
for
loop, the loop variable is reused for each iteration, so thei
variable is shared across all goroutines. That's not what we want. We need to make sure thati
is unique for each goroutine.
接着描述了两种解决方案:
- 将
i
的值作为参数传递给 goroutine 中的函数 - 在循环中创建一个新变量并改用它
由于您的 goroutine 是在您的循环之外启动的,因此只有 #2 适用于您的代码。
关于pointers - 通过 channel 发送指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49123133/