I have two goroutines independently producing data, each sending it to a channel. In my main goroutine, I\'d like to consume each of these outputs as they come in, but don\'t ca
When I came across such a need I took the below approach:
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
for p := range mins {
fmt.Println("Min:", p)
}
}()
go func() {
defer wg.Done()
for p := range maxs {
fmt.Println("Max:", p)
}
}()
wg.Wait()
I know this is not with single for select loop, but in this case I feel this is more readable without 'if' condition.
Close is nice in some situations, but not all. I wouldn't use it here. Instead I would just use a done channel:
for n := 2; n > 0; {
select {
case p := <-mins:
fmt.Println("Min:", p) //consume output
case p := <-maxs:
fmt.Println("Max:", p) //consume output
case <-done:
n--
}
}
Complete working example at the playground: http://play.golang.org/p/Cqd3lg435y
Your example solution would not work well. Once one of them closed, it would always be available for communication immediately. This means your goroutine will never yield and other channels may never be ready. You would effectively enter an endless loop. I posted an example to illustrate the effect here: http://play.golang.org/p/rOjdvnji49
So, how would I solve this problem? A nil channel is never ready for communication. So each time you run into a closed channel, you can nil that channel ensuring it is never selected again. Runable example here: http://play.golang.org/p/8lkV_Hffyj
for {
select {
case x, ok := <-ch:
fmt.Println("ch1", x, ok)
if !ok {
ch = nil
}
case x, ok := <-ch2:
fmt.Println("ch2", x, ok)
if !ok {
ch2 = nil
}
}
if ch == nil && ch2 == nil {
break
}
}
As for being afraid of it becoming unwieldy, I don't think it will. It is very rare you have channels going to too many places at once. This would come up so rarely that my first suggestion is just to deal with it. A long if statement comparing 10 channels to nil is not the worst part of trying to deal with 10 channels in a select.
Why not use goroutines? As your channels are getting closed, the whole thing turns into a simple range loop.
func foo(c chan whatever, prefix s) {
for v := range c {
fmt.Println(prefix, v)
}
}
// ...
go foo(mins, "Min:")
go foo(maxs, "Max:")
I wrote a package which provides a function to solve this problem (among several others):
https://github.com/eapache/channels
https://godoc.org/github.com/eapache/channels
Check out the Multiplex
function. It uses reflection to scale to an arbitrary number of input channels.