这个想法是在一个切片中有一个可变数量的通道,将通过它们接收的每个值推入一个通道,并在最后一个输入通道关闭后关闭该输出通道。这样的事情,但对于多个渠道超过两个:
func multiplex(cin1, cin2, cout chan int) {
n := 2
for {
select {
case v, ok := <-cin1:
if ok {
cout <- v
} else {
n -= 1
}
case v, ok := <-cin2:
if ok {
cout <- v
} else {
n -= 1
}
}
if n == 0 {
close(cout)
break
}
}
}
上面的代码避免了繁忙的循环,因为没有default
的情况,这很好(编辑:它看起来像“,ok”的存在使得select语句无阻塞,并且循环繁忙但是为了这个例子,可以把代码想象成阻止它。使用任意数量的输入通道也可以实现相同的功能吗?显然,这可以通过将切片成对地减少到单个通道来完成,但如果可能的话,我会对更简单的解决方案更感兴趣。
答案 0 :(得分:26)
我相信这段代码可以满足您的需求。我已经更改了签名,因此很明显输入和输出只能用于一个方向的通信。请注意添加sync.WaitGroup
,您需要某种方式让所有输入信号表明它们已经完成,这非常简单。
func combine(inputs []<-chan int, output chan<- int) {
var group sync.WaitGroup
for i := range inputs {
group.Add(1)
go func(input <-chan int) {
for val := range input {
output <- val
}
group.Done()
} (inputs[i])
}
go func() {
group.Wait()
close(output)
} ()
}
答案 1 :(得分:2)
编辑:添加成对缩减示例代码并重新排序部分答案。
首选解决方案是“不进行重组,以便您没有一条频道”。重组通常可以利用多个goroutine可以发送到单个通道的功能。因此,不要让每个源都在不同的通道上发送,然后必须处理从一堆通道接收,只需创建一个通道,让所有源都在该通道上发送。
Go不提供从一片频道接收的功能。这是一个经常被问到的问题,虽然刚刚给出的解决方案是首选,但有一些方法可以对其进行编程。我认为你在原始问题中提出的一个解决方案是说“减少成对切片”是一个二元分而治之的解决方案。只要您有将两个通道复用为一个的解决方案,这种方法就可以正常工作。您的示例代码非常接近工作。
你只是缺少一个让你的示例代码工作的小技巧。在递减n的位置,添加一行以将通道变量设置为nil。例如,我把代码读成了
case v, ok := <-cin1:
if ok {
cout <- v
} else {
n--
cin1 = nil
}
case v, ok := <-cin2:
if ok {
cout <- v
} else {
n--
cin2 = nil
}
}
此解决方案可以满足您的需求并且不会在等待。
那么,这个完整的例子将这个解决方案整合到一个多路复用切片的函数中:
package main
import (
"fmt"
"time"
)
func multiplex(cin []chan int, cout chan int) {
var cin0, cin1 chan int
switch len(cin) {
case 2:
cin1 = cin[1]
fallthrough
case 1:
cin0 = cin[0]
case 0:
default:
cin0 = make(chan int)
cin1 = make(chan int)
half := len(cin) / 2
go multiplex(cin[:half], cin0)
go multiplex(cin[half:], cin1)
}
for cin0 != nil || cin1 != nil {
select {
case v, ok := <-cin0:
if ok {
cout <- v
} else {
cin0 = nil
}
case v, ok := <-cin1:
if ok {
cout <- v
} else {
cin1 = nil
}
}
}
close(cout)
}
func main() {
cin := []chan int{
make(chan int),
make(chan int),
make(chan int),
}
cout := make(chan int)
for i, c := range cin {
go func(x int, cx chan int) {
for i := 1; i <= 3; i++ {
time.Sleep(100 * time.Millisecond)
cx <- x*10 + i
}
close(cx)
}(i, c)
}
go multiplex(cin, cout)
for {
select {
case v, ok := <-cout:
if ok {
fmt.Println("main gets", v)
} else {
return
}
}
}
}
答案 2 :(得分:0)
使用goroutines我制作了这个。这是你想要的吗?
package main
import (
"fmt"
)
func multiplex(cin []chan int, cout chan int) {
n := len(cin)
for _, ch := range cin {
go func(src chan int) {
for {
v, ok := <-src
if ok {
cout <- v
} else {
n-- // a little dangerous. Maybe use a channel to avoid missed decrements
if n == 0 {
close(cout)
}
break
}
}
}(ch)
}
}
// a main to test the multiplex
func main() {
cin := make([]chan int, 3)
cin[0] = make(chan int, 2)
cin[1] = make(chan int, 2)
cin[2] = make(chan int, 2)
cout := make(chan int, 2)
multiplex(cin, cout)
cin[1] <- 1
cin[0] <- 2
cin[2] <- 3
cin[1] <- 4
cin[0] <- 5
close(cin[1])
close(cin[0])
close(cin[2])
for {
v, ok := <-cout
if ok {
fmt.Println(v)
} else {
break
}
}
}
编辑:参考文献: