我有多个 goroutine 试图同时在同一个频道上接收。似乎在通道上开始接收的最后一个 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 中已经过时(见评论)
是的,这很复杂,但是有一些经验法则可以让事情变得更加简单。
这是应用这两个准则的程序的替代版本。这个案例展示了一个频道上的许多作者和一个读者:
c := make(chan string) for i := 1; i <= 5; i++ { go func(i int, co chan<- string) { for j := 1; j <= 5; j++ { co <- fmt.Sprintf("hi from %d.%d", i, j) } }(i, c) } for i := 1; i <= 25; i++ { fmt.Println(<-c) }
http://play.golang.org/p/quQn7xePLw
它创建了五个向单个通道写入的 go-routine,每个都写入了五次。主要的 go-routine 读取所有 25 条消息 - 您可能会注意到它们出现的顺序通常不是顺序的(即并发性很明显)。
这个例子演示了 Go 通道的一个特性:可以有多个作者共享一个通道;Go 将自动交错消息。
这同样适用于一个通道上的一个写入器和多个读取器,如这里的第二个示例所示:
c := make(chan int) var w sync.WaitGroup w.Add(5) for i := 1; i <= 5; i++ { go func(i int, ci <-chan int) { j := 1 for v := range ci { time.Sleep(time.Millisecond) fmt.Printf("%d.%d got %d\n", i, j, v) j += 1 } w.Done() }(i, c) } for i := 1; i <= 25; i++ { c <- i } close(c) w.Wait()
此第二个例子包括施加在主够程等待,否则退出及时并引起其他五个够程要提前终止(由于olov此校正)。
在这两个示例中,都不需要缓冲。将缓冲仅视为性能增强器通常是一个很好的原则。如果你的程序不会死锁没有缓冲区,也不会发生死锁与缓冲区是(但反之并不总是正确的)。因此,作为另一个经验法则,开始时不要缓冲,然后根据需要稍后添加。