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

时间:2018-08-09 14:05:25

标签: go concurrency channel

我刚接触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)
    }
}

我得到这样的输出:

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号,甚至没有将其放入通道中。为什么会这样?

1 个答案:

答案 0 :(得分:5)

不是。您的Println("PUT into channel")发生在 之后,将其放在通道上,这意味着在执行该打印语句之前,有机会从通道中读取它。

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

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

您对频道的读写操作按预期的顺序进行,只是您的打印语句使它看起来不正常。

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

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

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

  1. 执行是并发的,但对stdout的写入是同步的
  2. 每个函数调用和通道发送/接收都为调度程序提供了切换的机会,因此,即使使用GOMAXPROCS=1运行,它也可以在打印和通道操作(在读取器或写入器中)之间切换goroutine。

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