編輯:加入成對還原示例代碼和答案的重新排序部件。
首選的解決方案是「重組,以便您沒有渠道片」的非答案。重組通常可以使用多個goroutine可以發送到單個通道的功能。因此,不要讓每個來源都在不同的頻道上發送,然後不得不處理來自一堆頻道的接收,只需創建一個頻道,並讓所有來源在該頻道上發送即可。
Go沒有提供從一部分頻道接收的功能。這是一個經常被問到的問題,雖然剛剛給出的解決方案是首選,但也有編程的方法。我認爲你在原始問題中提出的「減少片段配對」的解決方案是一種二元分治法。這工作得很好,只要你有一個將兩個通道複用爲一個的解決方案。你的示例代碼非常接近工作。
你只是缺少一個讓你的示例代碼工作的小技巧。在你遞減n的地方,增加一條線將通道變量設置爲零。例如,我將代碼讀取爲
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
}
}
}
}
啊,非常好的解決方案,清晰簡潔。謝謝! – elpres
現在有一個包含函數的包(https://godoc.org/github.com/eapache/channels#Multiplex),它使用反射而不是多個goroutines來解決問題。 – Evan