douhun8647 2013-03-30 06:14 采纳率: 0%
浏览 148
已采纳

在一个通道上监听多个goroutine

I have multiple goroutines trying to receive on the same channel simultaneously. It seems like the last goroutine that starts receiving on the channel gets the value. Is this somewhere in the language spec or is it undefined behaviour?

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)

Output:

goroutine 4

Example On Playground

EDIT:

I just realized that it's more complicated than I thought. The message gets passed around all the goroutines.

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)

Output:

original, hi from 0, hi from 1, hi from 2, hi from 3, hi from 4

Example On Playground

  • 写回答

5条回答 默认 最新

  • drt96856 2013-03-30 17:30
    关注

    Yes, it's complicated, But there are a couple of rules of thumb that should make things feel much more straightforward.

    • prefer using formal arguments for the channels you pass to go-routines instead of accessing channels in global scope. You can get more compiler checking this way, and better modularity too.
    • avoid both reading and writing on the same channel in a particular go-routine (including the 'main' one). Otherwise, deadlock is a much greater risk.

    Here's an alternative version of your program, applying these two guidelines. This case demonstrates many writers & one reader on a channel:

    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

    It creates the five go-routines writing to a single channel, each one writing five times. The main go-routine reads all twenty five messages - you may notice that the order they appear in is often not sequential (i.e. the concurrency is evident).

    This example demonstrates a feature of Go channels: it is possible to have multiple writers sharing one channel; Go will interleave the messages automatically.

    The same applies for one writer and multiple readers on one channel, as seen in the second example here:

    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
    ", i, j, v)
                j += 1
            }
            w.Done()
        }(i, c)
    }
    
    for i := 1; i <= 25; i++ {
        c <- i
    }
    close(c)
    w.Wait()
    

    This second example includes a wait imposed on the main goroutine, which would otherwise exit promptly and cause the other five goroutines to be terminated early (thanks to olov for this correction).

    In both examples, no buffering was needed. It is generally a good principle to view buffering as a performance enhancer only. If your program does not deadlock without buffers, it won't deadlock with buffers either (but the converse is not always true). So, as another rule of thumb, start without buffering then add it later as needed.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(4条)

报告相同问题?

悬赏问题

  • ¥15 虚拟机打包apk出现错误
  • ¥30 最小化遗憾贪心算法上界
  • ¥15 用visual studi code完成html页面
  • ¥15 聚类分析或者python进行数据分析
  • ¥15 逻辑谓词和消解原理的运用
  • ¥15 三菱伺服电机按启动按钮有使能但不动作
  • ¥15 js,页面2返回页面1时定位进入的设备
  • ¥50 导入文件到网吧的电脑并且在重启之后不会被恢复
  • ¥15 (希望可以解决问题)ma和mb文件无法正常打开,打开后是空白,但是有正常内存占用,但可以在打开Maya应用程序后打开场景ma和mb格式。
  • ¥20 ML307A在使用AT命令连接EMQX平台的MQTT时被拒绝