Golang 缓冲通道在发送之前接收数据

Golang buffered channel receive data before even sent

我对 golang 很陌生。今天在测试通道在 Golang 中的工作方式时,我感到非常困惑。

根据教程:

Sends to a buffered channel block only when the buffer is full. Receives block when the buffer is empty.

我的测试程序是这样的:

package main

import "fmt"

func main() {
    ch := make(chan int, 2)

    go func(ch chan int) int {
        for i := 0; i < 10; i++ {
            fmt.Println("goroutine: GET ", <-ch)
        }
        return 1
    }(ch)

    for j := 0; j < 10; j++ {
        ch <- j
        fmt.Println("PUT into channel", j)
    }
}

我得到这样的输出:

PUT into channel 0
PUT into channel 1
goroutine: GET  0
goroutine: GET  1
goroutine: GET  2
PUT into channel 2
PUT into channel 3
PUT into channel 4
PUT into channel 5
goroutine: GET  3
goroutine: GET  4
goroutine: GET  5
goroutine: GET  6
PUT into channel 6
PUT into channel 7
PUT into channel 8
PUT into channel 9

请注意,数字 2 在放入频道之前就已从频道中获取。为什么会这样?

没有。你的 Println("PUT into channel") 发生在 之后 你把它放在频道上,这意味着它有机会在执行打印语句之前从频道中读取它。

示例输出中的实际执行顺序大致如下:

  1. Writer 例程将 2 写入通道。
  2. Reader 例程从通道接收 2
  3. Reader 例程打印 goroutine: GET 2.
  4. Writer 例程打印 PUT into channel 2

您的读写 from/to 频道正在按预期顺序进行,只是您的打印语句让它看起来乱序了。

如果您将作者的操作顺序更改为:

    fmt.Println("PUT into channel", j)
    ch <- j

您可能会看到更接近您预期的输出。但是,它仍然不一定完全代表操作顺序,因为:

  1. 并发执行,但写入 stdout 是同步的
  2. 每次函数调用和通道send/receive都是调度器切换的机会,所以即使运行GOMAXPROCS=1,它也可以在打印和通道操作之间切换goroutines(在reader 或作者)。

TL;DR:记录并发操作时,不要过多地阅读日志消息的顺序。