简体   繁体   English

Golang 中单个通道的多个发件人

[英]Multiple senders to single channel in Golang

The concept seems simple to explain, but a tad harder to implement ("correctly").这个概念似乎很容易解释,但实现起来有点困难(“正确”)。

The tl;dr is I want to run multiple functions that push output into a single channel. tl; dr 是我想运行多个将输出推送到单个通道的函数。

As a sample working test (with multiple channels), to elaborate on my question https://play.golang.org/p/1ztCvPFLXKv作为示例工作测试(具有多个通道),详细说明我的问题https://play.golang.org/p/1ztCvPFLXKv

package main

import (
    "fmt"
    "time"
)

type intTest struct {
    ID     int
    Number int
}

func modify1(channelID string, res chan []intTest) {
    s := []intTest{}
    for i := 0; i < 10; i++ {
        fmt.Printf("Adding inside: %s\n", channelID)
        s = append(s, intTest{i, 0})
        time.Sleep(100 * time.Millisecond)
    }
    res <- s
}
func modify2(channelID string, res chan []intTest) {
    s := []intTest{}
    for i := 10; i < 20; i++ {
        fmt.Printf("Adding inside: %s\n", channelID)
        s = append(s, intTest{i, 0})
        time.Sleep(200 * time.Millisecond)
    }
    res <- s
}
func modify3(channelID string, res chan []intTest) {
    s := []intTest{}
    for i := 20; i < 30; i++ {
        fmt.Printf("Adding inside: %s\n", channelID)
        s = append(s, intTest{i, 0})
        time.Sleep(300 * time.Millisecond)
    }
    res <- s
}

func main() {
    channelA := make(chan []intTest)
    channelB := make(chan []intTest)
    channelC := make(chan []intTest)

    go modify1("A", channelA)
    go modify2("B", channelB)
    go modify3("C", channelC)

    b := append(<-channelA, <-channelB...)
    b = append(b, <-channelC...)
    fmt.Println(b)
}

Output:输出:

Adding inside: C
Adding inside: A
Adding inside: B
..snip..
Adding inside: C
Adding inside: C
Adding inside: C
[{0 0} {1 0} {2 0} {3 0} {4 0} {5 0} {6 0} {7 0} {8 0} {9 0} {10 0} {11 0} {12 0} {13 0} {14 0} {15 0} {16 0} {17 0} {18 0} {19 0} {20 0} {21 0} {22 0} {23 0} {24 0} {25 0} {26 0} {27 0} {28 0} {29 0}]

However, I would like to achieve something like this: https://play.golang.org/p/qvC88LwkanY Output:但是,我想实现这样的目标: https : //play.golang.org/p/qvC88LwkanY输出:

Adding inside: C
Adding inside: A
Adding inside: B
..snip
Adding inside: B
Adding inside: A
Adding inside: C
[{0 0} {1 0} {2 0} {3 0} {4 0} {5 0} {6 0} {7 0} {8 0} {9 0}]

but as shown the functions modify2 & modify3 visually seems like they never get added.但如图所示,函数 modify2 和 modify3 在视觉上似乎从未被添加。

Is this possible, or is the top sample more feasible?这是可能的,还是顶部样本更可行?

I would like to achieve something like this我想实现这样的目标

channelA := make(chan []intTest)
go modify1("A", channelA)
go modify2("B", channelA)
go modify3("C", channelA)

Is this possible, or is the top sample more feasible?这是可能的,还是顶部样本更可行?

Yes: you can use a single channel across multiple goroutines -- that's what channels are designed for.是的:您可以跨多个 goroutine 使用单个通道——这就是通道的设计目的。

but as shown the functions modify2 & modify3 visually seems like they never get added.但如图所示,函数 modify2 和 modify3 在视觉上似乎从未被添加。

The problem you have there is that you only called the receive operator once:您遇到的问题是您只调用了接收运算符一次:

b := append(<-channelA)
fmt.Println(b)

The other two goroutines were either blocked waiting to send their result, or still building their result, when your main goroutine exited.当您的main goroutine 退出时,另外两个 goroutine 要么被阻塞,等待发送它们的结果,要么仍在构建它们的结果。

If you change your main() function to this, you can see that all three workers will send their results over the channel if another routine is ready to receive (because you used an unbuffered channel, sends will block until a receiver is ready):如果您将main()函数更改为此,您可以看到如果另一个例程准备接收(因为您使用了无缓冲通道,发送将阻塞,直到接收器准备就绪),所有三个工作器都将通过通道发送它们的结果:

func main() {
    ch := make(chan []intTest)

    go modify1("A", ch)
    go modify2("B", ch)
    go modify3("C", ch)
    fmt.Println(<-ch)
    fmt.Println(<-ch)
    fmt.Println(<-ch)
}

Which outputs:哪些输出:

Adding inside: C
Adding inside: A
Adding inside: B
Adding inside: A
Adding inside: A
Adding inside: B
Adding inside: C
Adding inside: A
Adding inside: B
Adding inside: A
Adding inside: A
Adding inside: B
Adding inside: C
Adding inside: A
Adding inside: A
Adding inside: B
Adding inside: A
Adding inside: C
Adding inside: A
Adding inside: B
[{0 0} {1 0} {2 0} {3 0} {4 0} {5 0} {6 0} {7 0} {8 0} {9 0}]
Adding inside: C
Adding inside: B
Adding inside: B
Adding inside: C
Adding inside: B
Adding inside: C
Adding inside: B
[{10 0} {11 0} {12 0} {13 0} {14 0} {15 0} {16 0} {17 0} {18 0} {19 0}]
Adding inside: C
Adding inside: C
Adding inside: C
[{20 0} {21 0} {22 0} {23 0} {24 0} {25 0} {26 0} {27 0} {28 0} {29 0}]

You can then change that code to append received elements to a single list prior to output, or otherwise format the received elements in whatever way you like.然后,您可以更改该代码以在输出之前将接收到的元素附加到单个列表中,或者以您喜欢的任何方式格式化接收到的元素。

I thought it's almost like popping onto a stack and then pulling the whole stack我认为这几乎就像弹出一个堆栈然后拉整个堆栈

With an initialized, not-closed, buffered channel, a send statement puts one element onto a queue, and a receive operation pops one element off the queue and returns it.使用初始化的、未关闭的、缓冲的通道,发送语句将一个元素放入队列,接收操作从队列中弹出一个元素并将其返回。 It's first in first out (FIFO) order, so it's a queue rather than a stack.它是先进先出 (FIFO) 顺序,所以它是一个队列而不是一个堆栈。 In the examples above the channel is unbuffered, so a send has to wait for a goroutine ready to receive, and a receive has to wait for a goroutine ready to send.在上面的例子中,通道是无缓冲的,所以发送必须等待准备接收的 goroutine,接收必须等待准备发送的 goroutine。 Main is a goroutine as well. Main 也是一个 goroutine。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM