An efficient transition gives this example on how to emulate a semaphore with channels:
var sem = make(chan int, MaxOutstanding) func handle(r *Request) { <-sem process(r) sem <- 1 } func init() { for i := 0; i < MaxOutstanding; i++ { sem <- 1 } } func Serve(queue chan *Request) { for { req := <-queue go handle(req) } }
It also says: since data is synchronized when it is received from the channel (that is, the transmission "occurs before" the receive, see Go memory model) ., The semaphore must be received on the channel, not on the send.
Now, I think, I understand the Go memory model and the definition of "comes earlier". But I do not see that there is a problem with blocking on the send channel:
func handle(r *Request) { sem <- 1 process(r) <-sem } func init() {}
This code (with sem and Serve unchanged above) uses the buffer channel in the reverse order. The channel is empty. When you handle sending will be blocked if there are already MaxOutstanding goroutines that perform this process. As soon as one of them finishes processing and "releases" the slot from the channel, having received one int, our sending will be unlocked, and goroutine will begin its own processing.
Why is this a bad way to do synchronization, as the tutorial seems to imply?
Does the receive operation that frees the channel slot have not “occurred before” the send that will use the same slot? How is this possible?
In other words, the Language Reference says that "sending on the buffer channel [blocks until] there is room in the buffer".
But the Memory Model only says that "Receiving from an unbuffered channel occurs before the transmission on this channel is completed." In particular, he does not say that reception from a buffered channel, which is complete , occurs before the transmission on this channel is completed.
Is this some kind of corner case that cannot be trusted to do the right thing? (which actually synchronizes the sending that was blocked by the reception that blocks it)
If this is the case, it looks like an unpleasant race condition in a language designed to minimize hidden race conditions: - (
var c = make(chan int, 1) var a string func f() { a = "hello, world" <-c