breaking out of a select statement when all channels are closed
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.
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
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:")