编辑:添加了成对减少示例代码并重新排序了部分答案。
首选的解决方案是不回答“重组,使您没有渠道切片”。重构通常可以利用多个 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 v := range cout {
fmt.Println("main gets", v)
}
}