我有多个 goroutines 试图同时在同一个频道上接收。似乎最后一个开始在通道上接收的 goroutine 获得了价值。这是语言规范中的某个地方还是未定义的行为?
c := make(chan string)
for i := 0; i < 5; i++ {
go func(i int) {
<-c
c <- fmt.Sprintf("goroutine %d", i)
}(i)
}
c <- "hi"
fmt.Println(<-c)
输出:
goroutine 4
编辑:
我才意识到这比我想象的要复杂。消息在所有 goroutine 之间传递。
c := make(chan string)
for i := 0; i < 5; i++ {
go func(i int) {
msg := <-c
c <- fmt.Sprintf("%s, hi from %d", msg, i)
}(i)
}
c <- "original"
fmt.Println(<-c)
输出:
original, hi from 0, hi from 1, hi from 2, hi from 3, hi from 4
注意: 以上输出在较新版本的 Go 中已过时(请参阅评论)
原文由 Ilia Choly 发布,翻译遵循 CC BY-SA 4.0 许可协议
是的,这很复杂,但是有一些经验法则应该会让事情变得更简单。
这是您的程序的替代版本,应用了这两个准则。这个案例展示了一个频道上的许多作者和一个读者:
http://play.golang.org/p/quQn7xePLw
它创建了五个写入单个通道的 go-routines,每个 go-routines 写入五次。主要的 go-routine 读取所有 25 条消息——你可能会注意到它们出现的顺序通常不是连续的(即并发是显而易见的)。
这个例子演示了 Go channels 的一个特性:可以让多个作者共享一个 channel; Go 将自动交错消息。
这同样适用于一个频道上的一位作者和多个读者,如此处的第二个示例所示:
第二个示例 包括对主 goroutine 施加的等待,否则它会立即退出并导致其他五个 goroutine 提前终止 (感谢 olov 进行此更正) 。
在这两个示例中,都不需要缓冲。将缓冲仅视为性能增强器通常是一个很好的原则。如果您的程序在 没有 缓冲区的情况下不会死锁,那么它也不会 在有 缓冲区的情况下死锁(但反之 并不 总是正确的)。所以,作为 另一个经验法则,开始时不要缓冲,然后根据需要添加它。