I am trying to use channels to implement a kind of a worker pool. Please take a look at the code below
https://play.golang.org/p/g7aKxDoP9lf (The Go 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)
}
}
}
Here is a sample output
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
It seems that at the time when the pointer is recieved by worker2, the value has already changed in the original variable which is reflected in the value printed.
The question is how can this be avoided? How can this be worked around?
The value that the received pointer points to is not what you expect because you're sending it a pointer to the same variable every time, so the worker sees whatever value that variable has at the time it dereferences the pointer. A typical way around this sort of problem is to make a copy of the variable inside the for
loop and send a pointer to that. That way, you're sending a pointer to a different object every time. Try this:
for i := 0; i < 10; i++ {
fmt.Printf("sending: %v\n", i)
iCopy := i
q2 <- &iCopy
}