Select
Multiplexing Channels
The select statement lets a goroutine wait on multiple channel operations simultaneously. It is like a switch statement, but each case is a channel send or receive.
select {
case msg := <-ch1:
fmt.Println("from ch1:", msg)
case msg := <-ch2:
fmt.Println("from ch2:", msg)
}
If multiple channels are ready, select picks one at random. If none are ready, it blocks until one becomes ready.
"On screen!" The bridge crew monitors tactical, science, and communications all at once --- just like
selectlistens on multiple channels simultaneously, ready to act on whichever one has news first.
Non-Blocking Operations
Add a default case to make channel operations non-blocking:
select {
case msg := <-ch:
fmt.Println("received:", msg)
default:
fmt.Println("no message available")
}
Without default, the select blocks. With default, it executes the default case immediately if no channel is ready.
Pattern: Merging Channels
A common use of select is merging multiple channels into one. This lets a consumer read from a single channel regardless of how many producers exist:
func merge(ch1, ch2 <-chan string) <-chan string {
out := make(chan string)
go func() {
defer close(out)
for ch1 != nil || ch2 != nil {
select {
case v, ok := <-ch1:
if !ok { ch1 = nil; continue }
out <- v
case v, ok := <-ch2:
if !ok { ch2 = nil; continue }
out <- v
}
}
}()
return out
}
Setting a channel to nil after it closes prevents select from receiving zero values from it, because receives on a nil channel block forever.
Your Task
Write a function merge(ch1, ch2 <-chan int) <-chan int that merges two integer channels into one. The output channel should receive all values from both input channels and close when both inputs are exhausted.
Key steps:
- Create an output channel and launch a goroutine
- Loop while at least one input channel is still open
- Use
selectto receive from whichever channel is ready - When a channel is closed (
okis false), set it tonilsoselectignores it - Close the output channel when both inputs are exhausted