我有多个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
是的,它很复杂,但是有一些经验法则可以使事情变得简单得多。
这是应用这两个准则的程序的替代版本。此案例演示了频道上的许多作家和一位读者:
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例程写入单个通道,每个例程写入五次。主例程会读取所有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]此校正)_。
在两个示例中,都不需要缓冲。通常,将缓冲仅视为性能增强器是一个很好的原则。如果你的程序不会死锁 没有 缓冲区,也不会发生死锁 与 缓冲区是(但反之 并不 总是正确的)。因此,作为 另一条经验法则,开始时不要缓冲,然后根据需要稍后添加 。