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

fuy*_*uyi 0 concurrency channel go

我对golang很新.今天在测试Golang如何在Golang中运行时,我感到非常困惑.

根据教程:

仅在缓冲区已满时才发送到缓冲的通道块.缓冲区为空时接收阻止.

我的测试程序如下所示:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

我得到这样的输出:

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
Run Code Online (Sandbox Code Playgroud)

请注意,在放入通道之前,从通道获取数字2.为什么会这样?

Adr*_*ian 5

它没有.你的Println("PUT into channel")情况后,你把它放在通道上,这意味着有它从通道被执行的打印语句之前读的机会.

样本输出中的实际执行顺序如下:

  1. Writer例程写入2通道.
  2. 读者例程2从频道接收.
  3. 读者例程打印goroutine: GET 2.
  4. 作家例程打印 PUT into channel 2

您对通道的读写操作是按预期顺序进行的,只是您的打印语句使其看起来无序.

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

    fmt.Println("PUT into channel", j)
    ch <- j
Run Code Online (Sandbox Code Playgroud)

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

  1. 执行是并发的,但写入stdout是同步的
  2. 每个函数调用和信道发送/接收是调度切换的机会,所以即使与运行GOMAXPROCS=1,它可以打印和通道操作(在读或写)之间切换够程.

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