一尘不染

在一个通道上监听多个goroutine

go

我有多个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

操场上的例子


阅读 298

收藏
2020-07-02

共1个答案

一尘不染

是的,它很复杂,但是有一些经验法则可以使事情变得简单得多。

  • 宁愿使用 传递给go-routine 的通道的形式参数, 而不是访问全局范围内的通道。您可以通过这种方式获得更多的编译器检查,以及更好的模块化。
  • 避免在特定的例行程序 (包括“主要” 程序)的同一通道上进行读取和写入 。否则,死锁风险更大。

这是应用这两个准则的程序的替代版本。此案例演示了频道上的许多作家和一位读者:

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]此校正)_。

在两个示例中,都不需要缓冲。通常,将缓冲仅视为性能增强器是一个很好的原则。如果你的程序不会死锁 没有 缓冲区,也不会发生死锁 缓冲区是(但反之
并不 总是正确的)。因此,作为 另一条经验法则,开始时不要缓冲,然后根据需要稍后添加

2020-07-02