Incomprehension of buffered channels described in the "Concurrency in Go" book

Viewed 69

I read the book "Concurrency in Go" written by Katherine Cox-Buday and I don't understand comments for examples of buffered channels.

The author says:

if a goroutine making writes to a channel has knowledge of how many writes it will make,
it can be useful to create a buffered channel whose capacity is the number of writes to be made

It sounds clear, but examples are confusing.

  1. First example - source: https://github.com/kat-co/concurrency-in-go-src/blob/master/gos-concurrency-building-blocks/channels/fig-using-buffered-chans.go#L13
    var stdoutBuff bytes.Buffer         // <1>
    defer stdoutBuff.WriteTo(os.Stdout) // <2>

    intStream := make(chan int, 4) // <3>
    go func() {
        defer close(intStream)
        defer fmt.Fprintln(&stdoutBuff, "Producer Done.")
        for i := 0; i < 5; i++ {
            fmt.Fprintf(&stdoutBuff, "Sending: %d\n", i)
            intStream <- i
        }
    }()

    for integer := range intStream {
        fmt.Fprintf(&stdoutBuff, "Received %v.\n", integer)
    }

The line with comment <3> has the following explanation:

Here we create a buffered channel with a capacity of one.

There is 4, not 1. Is it a mistake?

  1. Second example - channel ownership, source: https://github.com/kat-co/concurrency-in-go-src/blob/master/gos-concurrency-building-blocks/channels/fig-chan-ownership.go
    chanOwner := func() <-chan int {
        resultStream := make(chan int, 5) // <1>
        go func() {                       // <2>
            defer close(resultStream) // <3>
            for i := 0; i <= 5; i++ {
                resultStream <- i
            }
        }()
        return resultStream // <4>
    }

The line marked as <1>, has the following comment:

Since we know we will produce six results, we create a buffered channel of five
so that the goroutine can complete as quickly as possible.

I completely don't understand this comment. Goroutine will be blocked, because the channel has the capacity of 5 messages and there will be produced 6 messages, so it will wait until a receiver takes the first message.

3 Answers

Since we know we will produce six results, we create a buffered channel of five so that the goroutine can complete as quickly as possible.

You are correct that the goroutine will block until a value is received.

It doesn't make sense to create a channel with capacity one less than the number of values to be sent. The goroutine can be eliminated if the channel capacity is equal to or greater than the number of values:

chanOwner := func() <-chan int {
    resultStream := make(chan int, 6)
    for i := 0; i < cap(resultStream); i++ {
        resultStream <- i
    }
    close(resultStream)
    return resultStream
}()

or this by eliminating the anonymous function:

    chanOwner := make(chan int, 6)
    for i := 0; i < cap(chanOwner); i++ {
        resultStream <- i
    }
    close(chanOwner)

Yes, it sounds like this book needs a better editor!


the channel capacity is indeed indicated as the 2nd argument to make:

intStream := make(chan int, 4) // buffered-channel of capacity 4 (not 1!)

If no reads are done on the channel - then yes the writing goroutine will write 5 times to the buffered channel (of capacity 5) without issue (i.e. without blocking). The 6th write will indeed block - and the goroutine will not return until the buffer congestion reduces.

If some other goroutine does read from the channel - even just once - then the buffer frees up and the writer goroutine will be able to complete the final write.

There is 4, not 1. Is it a mistake?

It seems a typo. As clearly stated in the documentation, the second argument to make is the channel capacity:

Channel: The channel's buffer is initialized with the specified buffer capacity. If zero, or the size is omitted, the channel is unbuffered.

Therefore make(chan int, 4) is a chan with capacity 4.

Goroutine will be blocked, because the channel has the capacity of 5 messages and there will be produced 6 messages, so it will wait until a receiver takes the first message.

Correct, the declared chan has capacity 5, and if there's no receiver, the sixth and last send operation will indeed block, as the channel buffer is full.

Assuming good faith on all sides, the book probably missed a round of proofreading.

Related