2

I understand that usually, if I wish to access an out-of-scope variable from a Go routine, it is my responsibility to create a copy to be conceptually owned by the Go routine. Is this also true for channels, or are those exempt?

Effective Go #channels explains this with the words "it may seem odd to write req := req but it's a [sic] legal and idiomatic in Go to do this," referring to this code example:

var sem = make(chan int, MaxOutstanding)
// (other code, filling sem, defining process(..), etc., omitted)

func Serve(queue chan *Request) {
    for req := range queue {
        <-sem
        req := req // Create new instance of req for the goroutine.
        go func() {
            process(req)
            sem <- 1
        }()
    }
}

I happened to have very nearly replicated this example code in my own project (except that I am using a chan struct{} rather than chan int for my semaphore, and declare it local to the Serve func). Staring at it, I am wondering if accessing the same channel from multiple concurrent goroutines is really fine, or if something like sem := sem is called for.

  • 1
    This question is similar to some others - you may find them informative too. [Reading from multiple channels simultaneously in Golang](http://stackoverflow.com/questions/20593126/reading-from-multiple-channels-simultaneously-in-golang) and [multiple goroutines listening on one channel](http://stackoverflow.com/questions/15715605/multiple-goroutines-listening-on-one-channel) – Rick-777 May 14 '14 at 17:43

2 Answers2

5

Yes, there is no issue with using the same channel from multiple goroutines, it's quite normal to do so.

The reason for the req := req is not for the benefit of the goroutine, but is instead required to prevent unexpected behavior within the closure. Without it, each iteration of the loop will change the value of req within the closure rather than giving it a unique value each time.

A quick example of this effect can be found here: http://play.golang.org/p/ylRQkh2SeC

Innominate
  • 2,643
  • 2
  • 17
  • 8
  • Thanks for taking the time to answer my question! However: Just such a race condition ("unexpected behavior") as you mention for `req` could be feared inside whatever the current or future implementation of `chan` does internally. If you've answered my question depends on the interpretation of "same channel," which I consider ambiguous in this context. To illustrate: If I pass the channel as an function argument to the closure, is it still the same channel? If yes, you haven't answered my question; if no, then what channel is it then? –  May 09 '14 at 21:57
  • A channel is just a value. You can pass that value around all you like. It will always refer to the same channel that can be read or written to by any goroutine with access to it. This is generally how you should share channels between goroutines as changing the variable containing a channel value can cause its own race conditions if multiple goroutines are using it. – Innominate May 09 '14 at 22:28
  • 1
    pyramids, worth emphasizing that Innominate's unexpected behavior has to do with the closure, not the goroutines. Even with GOMAXPROCS=1, all of the functions will look at the same `req` variable as if it were a global. But if you run `req := req` to define a new variable each pass through the loop, every closure gets its own `req` variable and all is well. A demonstration with no `go` statements is at http://play.golang.org/p/aW_C7Xwub6 if that helps. – twotwotwo May 09 '14 at 23:13
  • I know, @twotwotwo. That wasn't my question, though. –  May 10 '14 at 14:42
4

Channel accesses are thread-safe, so you do not need to lock it or make local copies of it or anything like that.

Evan
  • 6,369
  • 1
  • 29
  • 30