Search code examples
gochannelunbuffered

Go Channels behaviour appears inconsistent


I am seeing an inconsistency in the way unbuffered channels appear to work - this is either an inconsistency in Go, or in my understanding of Go...

Here is a simple example with output. The 'inconsistency' is with the 'make channel' lines.

package main
import (
    "fmt"
    )

func send(sendto chan string) {
    fmt.Println("send 1")
    sendto <- "Hello"
    fmt.Println("send 2")
    sendto <- "World"
    fmt.Println("send 3")
    sendto <- ""
    fmt.Println("send() exit")
}

func main() {
    //hole := make(chan string)
    //hole := make(chan string, 0)
    hole := make(chan string, 1)
    go send(hole)
    fmt.Println("main loop")
    carryon := true
    for carryon {
        msg := <- hole
        if msg == "" {
            carryon = false
        } else {
            fmt.Println(" recd ", msg)
        }
    }
}

When I run as above, the output is as expected (and also as expected for a buffer size of 2). i.e. the channel has a buffer of 1, which holds one value - on next attempting to write, there is a context switch to the main to allow it to consume the first value.

main loop
send 1
send 2
 recd  Hello
send 3
 recd  World
send() exit

When I then change the make channel line to:

hole := make(chan string, 0)

The output is:

main loop
send 1
send 2
 recd  Hello
 recd  World
send 3
send() exit

I would have expected the send 2 and the recd Hello to be the other way around...

I get the same output for hole := make(chan string)

I checked the specification and it says

The capacity, in number of elements, sets the size of the buffer in the channel. If the capacity is zero or absent, the channel is unbuffered and communication succeeds only when both a sender and receiver are ready. Otherwise, the channel is buffered and communication succeeds without blocking if the buffer is not full (sends) or not empty (receives).

Please can someone explain either

  • Why my expectations are wrong - please be kind
  • or whether Go is actually wrong

Thank you


Solution

  • Roughly: Send and receive happen concurrently. The details are explained in the Go Memory Model which determines this behaviour. Concurrent code is complicated...