0

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

这是一个带有输出的简单示例。“不一致”与“制作频道”行有关。

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 2and therecd Hello会反过来……

我得到相同的输出hole := make(chan string)

我检查了规范,它说

容量(以元素数量计)设置通道中缓冲区的大小。如果容量为零或不存在,则通道是无缓冲的,并且只有在发送方和接收方都准备好时通信才会成功。否则,如果缓冲区未满(发送)或非空(接收),则通道被缓冲并且通信成功而不会阻塞。

请有人解释一下

  • 为什么我的期望是错误的 - 请善待
  • 或者 Go 是否真的错了

谢谢

4

3 回答 3

3

这两个 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 2之前打印,recd Hello因为 send() 在运行时调度 main() 再次运行之前运行到 print 语句。

打印两条消息的关系之前没有发生。它们可以按任意顺序打印。

于 2015-01-28T19:30:23.097 回答
2

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

于 2015-01-28T19:30:11.867 回答
1

只有当发送者和接收者都准备好时,通信才会成功

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

于 2015-01-28T19:30:15.267 回答