Go by Example: Select Statements
Coordinate concurrency with the Select Statement, Go's control structure for waiting on multiple channel operations. This example demonstrates how to use `select` to handle asynchronous events, implement timeouts, and perform non-blocking channel operations.
Code
package main
import (
"fmt"
"time"
)
func main() {
c1 := make(chan string)
c2 := make(chan string)
// Goroutine 1
go func() {
time.Sleep(1 * time.Second)
c1 <- "one"
}()
// Goroutine 2
go func() {
time.Sleep(2 * time.Second)
c2 <- "two"
}()
// Wait for both
for i := 0; i < 2; i++ {
select {
case msg1 := <-c1:
fmt.Println("received", msg1)
case msg2 := <-c2:
fmt.Println("received", msg2)
}
}
}Explanation
The select statement is the backbone of concurrency in Go, acting like a switch statement for channels. It allows a goroutine to wait on multiple communication operations simultaneously, blocking until one of the cases is ready to proceed. This mechanism is crucial for coordinating complex concurrent workflows, such as handling timeouts, processing data from multiple sources, or implementing non-blocking operations.
Unlike a standard switch, select cases refer to channel communications (send or receive). If multiple cases are ready at the same time, select chooses one at random to execute, ensuring fairness and preventing starvation. If a default case is provided, it executes immediately if no other channel is ready, enabling non-blocking sends or receives.
Key behaviors and patterns include:
- Blocking: Without a default case,
selectblocks until a channel is ready. - Random Selection: Uniformly chooses from ready channels to avoid bias.
- Nil Channels: Operations on nil channels block forever; in a
select, that case is simply ignored (disabled). - Timeouts: Combining
time.Afterwithselectallows for robust timeout logic.

