我发现无缓冲通道的工作方式不一致 - 这可能是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 2
和recd Hello
成为另一种方式......
我得到hole := make(chan string)
我检查了规范并说明了
容量(以元素数量)设置通道中缓冲区的大小。如果容量零或不存在,则频道无缓冲,仅当发送方和接收方都准备好时,通信才会成功。否则,如果缓冲区未满(发送)或非空(接收),则通道被缓冲并且通信成功而不会阻塞。
请有人解释
谢谢
答案 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继续运行,直到它再次尝试发送,此时接收器未准备好,因此调用调度程序等。