Go Channels 行为看起来不一致

Go Channels behaviour appears inconsistent

我发现无缓冲通道的工作方式不一致 - 这要么是 Go 中的不一致,要么是我对 Go 的理解...

这是一个带有输出的简单示例。 'inconsistency' 与 'make channel' 行。

package main
import (
    "fmt"
    )

func send(sendto chan string) {
    fmt.Println("send 1")
    sendto <- "Hello"
    fmt.Println("send 2")
    sendto <- "World"
    fmt.Println("send 3")
    sendto <- ""
    fmt.Println("send() exit")
}

func main() {
    //hole := make(chan string)
    //hole := make(chan string, 0)
    hole := make(chan string, 1)
    go send(hole)
    fmt.Println("main loop")
    carryon := true
    for carryon {
        msg := <- hole
        if msg == "" {
            carryon = false
        } else {
            fmt.Println(" recd ", msg)
        }
    }
}

当我如上所述 运行 时,输出符合预期(对于缓冲区大小为 2 的情况也符合预期)。即通道有一个缓冲区 1,它保存一个值 - 在下一次尝试写入时,有一个上下文切换到 main 以允许它使用第一个值。

main loop
send 1
send 2
 recd  Hello
send 3
 recd  World
send() exit

当我将 make 通道行更改为:

hole := make(chan string, 0)

输出为:

main loop
send 1
send 2
 recd  Hello
 recd  World
send 3
send() exit

我原以为 send 2recd Hello 会相反...

我得到与 hole := make(chan string)

相同的输出

我查看了规格,上面写着

The capacity, in number of elements, sets the size of the buffer in the channel. If the capacity is zero or absent, the channel is unbuffered and communication succeeds only when both a sender and receiver are ready. Otherwise, the channel is buffered and communication succeeds without blocking if the buffer is not full (sends) or not empty (receives).

谁能解释一下

谢谢

大致:发送和接收同时发生。详细信息在确定此行为的 Go 内存模型中进行了解释。并发代码复杂...

communication succeeds only when both a sender and receiver are ready

关键是这不需要接收方立即开始处理它收到的消息。特别是在您的情况下,它已准备就绪,因此它无需调用调度程序即可接收值(无上下文切换)。 goroutine 继续 运行 直到它再次尝试发送,此时接收器还没有准备好,因此调用调度程序等

这两个 goroutine 的时间轴显示了正在发生的事情:

send()                  main()

fmt.Println("send 1")
sendto <- "Hello"       msg := <- hole              // sender and receiver both ready
fmt.Println("send 2")
                        fmt.Println(" recd ", msg)  // msg is "Hello"
sendto <- "World"       msg := <- hole              // sender and receiver both ready
                        fmt.Println(" recd ", msg)  // msg is "World"
fmt.Println("send 3")
sendto <- ""
fmt.Println("send() exit")

send 2recd Hello 之前打印,因为 send() 运行s 在 运行 时间安排 main() 到 运行 之前打印语句再次。

打印这两条消息没有happens before关系。它们可以按任一顺序打印。