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

时间:2018-02-23 21:14:08

标签: go concurrency channels

这个概念似乎很容易解释,但有点难以实现("正确")。

tl; dr是我想运行多个函数将输出推送到单个通道。

作为样本工作测试(有多个频道),详细说明我的问题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)
}

输出:

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}]

但是,我想实现这样的目标: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}]

但是如图所示,函数modify2&amp;视觉上的modify3似乎永远不会被添加。

这是可能的,还是顶级样本更可行?

1 个答案:

答案 0 :(得分:1)

  

我想实现这样的目标

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

这是可能的,还是顶级样本更可行?

是的:您可以在多个goroutine中使用单个通道 - 这就是为其设计的通道。

  

但是如图所示,函数modify2&amp;视觉上的modify3似乎永远不会被添加。

你遇到的问题是你只调用了一次接收运算符:

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

当你的main goroutine退出时,其他两个goroutine要么被阻止等待发送他们的结果,要么仍在建立他们的结果。

如果您将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)
}

哪个输出:

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}]

然后,您可以更改该代码,以便在输出之前将接收到的元素附加到单个列表,或以其他任何方式格式化接收到的元素。

  

我认为这几乎就像弹出一个堆叠,然后拉动整个堆栈

使用初始化的,未关闭的缓冲通道,send语句将一个元素放入队列,接收操作从队列中弹出一个元素并返回它。它是先进先出(FIFO)顺序,所以它是队列而不是堆栈。在上面的示例中,通道是无缓冲的,因此发送必须等待goroutine准备好接收,并且接收必须等待准备发送的goroutine。主要是goroutine。