Select patterns
Use `select` to express multiplexed communication and cancellation clearly; avoid busy loops and fairness assumptions.
Canonical guidance
- keep each
selectarm meaningful and bounded - include cancellation paths in long-lived loops
- do not assume deterministic fairness across ready cases
Use when
- cancellation-aware workers
- multiplexing multiple channels
- timeouts and shutdown signals
Avoid
defaultbranches that spin and burn CPU- giant
selectblocks mixing unrelated concerns - assuming one ready case always wins first
Preferred pattern
for {
select {
case <-ctx.Done():
return ctx.Err()
case item, ok := <-in:
if !ok {
return nil
}
handle(item)
}
}
Anti-pattern
- adding a
defaultjust to avoid blocking when the caller actually wanted backpressure
Explanation: This anti-pattern is tempting because non-blocking code feels safer, but it often turns coordination into dropped work or CPU spin.
Why
selectis precise when it models real coordination, not when it is used to dodge blocking semantics
Related pages
Sources
- The Go Programming Language Specification - Go Team
- Advanced Go Concurrency Patterns - Sameer Ajmani