Go Channels行为似乎不一致

时间:2015-01-28 19:11:41

标签: go channel unbuffered

我发现无缓冲通道的工作方式不一致 - 这可能是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 loop
send 1
send 2
 recd  Hello
send 3
 recd  World
send() exit

然后我将制作频道行更改为:

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)

的相同输出

我检查了规范并说明了

  

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

请有人解释

  • 为什么我的期望错了 - 请善待
  • 或Go是否实际上是错误的

谢谢

3 个答案:

答案 0 :(得分:3)

这两个goroutines的时间表显示了正在进行的事情:

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语句。

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

答案 1 :(得分:2)

大致:发送和接收同时发生。详细信息在Go Memory Model中解释,它决定了这种行为。并发代码很复杂......

答案 2 :(得分:1)

  

只有当发送方和接收方都准备就绪时,通信才会成功

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