Search code examples
pointersgogoroutinechannel

Sending pointers over a channel


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?


Solution

  • 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
    }