"Concurrency in Go"书中描述的缓冲通道的不理解

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

我读了 Katherine Cox-Buday 写的“Concurrency in Go”一书,我不明白缓冲通道示例的评论。

作者说:

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

听起来很清楚,但示例令人困惑。

  1. 第一个示例 - 来源: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)
    }

注释<3>的那一行解释如下:

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

有4个,不是1个,搞错了吗?

  1. 第二个示例 - 频道所有权,来源: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>
    }

标记为 <1> 的行具有以下注释:

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.

我完全不明白这个评论。 goroutine 会被阻塞,因为 channel 有 5 条消息的容量,会产生 6 条消息,所以它会等到接收者接收到第一条消息。

是的,听起来这本书需要更好的编辑!


信道容量确实被指示为 make 的第二个参数:

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

如果没有在通道上进行读取 - 那么是的,写入 goroutine 将向缓冲通道(容量为 5)写入 5 次而没有问题(即没有阻塞)。第 6 次写入确实会阻塞 - 在缓冲区拥塞减少之前,goroutine 不会 return。

如果其他一些 goroutine 确实从通道读取 - 即使只是一次 - 然后缓冲区释放并且 writer goroutine 将能够完成最后的写入。

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

好像打错了。正如文档中明确指出的那样,make 的第二个参数是通道容量:

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

因此make(chan int, 4)是一个容量为4的chan。

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.

正确,声明的通道容量为 5,如果没有接收方,第六次也是最后一次发送操作确实会阻塞,因为通道缓冲区已满。

假设各方都有诚意,这本书可能错过了一轮校对。

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.

你是正确的,goroutine 将阻塞直到收到一个值。

创建一个容量比要发送的值的数量少一的通道是没有意义的。如果通道容量等于或大于值的个数,则可以淘汰goroutine:

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

或者通过消除匿名函数:

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