Skip to content

Go Select

Go's select statement lets you wait on multiple channel operations. Combining select with goroutines and channels is a powerful feature of Go.

1. Basic Example

select blocks until one of its cases can run, then it executes that case. It's like a switch statement but for channels.

package main

import (
    "fmt"
    "time"
)

func main() {
    c1 := make(chan string)
    c2 := make(chan string)

    go func() {
        time.Sleep(1 * time.Second)
        c1 <- "one"
    }()
    go func() {
        time.Sleep(2 * time.Second)
        c2 <- "two"
    }()

    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-c1:
            fmt.Println("received", msg1)
        case msg2 := <-c2:
            fmt.Println("received", msg2)
        }
    }
}

2. Timeouts

You can use select with time.After to prevent a program from waiting forever on a channel.

1
2
3
4
5
6
select {
case res := <-c1:
    fmt.Println(res)
case <-time.After(1 * time.Second):
    fmt.Println("timeout 1")
}

3. Non-Blocking Channel Operations

If a default case is present, the select statement becomes non-blocking. It will try to send/receive, and if it can't, it immediately executes the default code.

1
2
3
4
5
6
select {
case msg := <-messages:
    fmt.Println("received message", msg)
default:
    fmt.Println("no message received")
}

Important Notes

  1. Fairness: If multiple channels are ready at the same time, Go picks one at random.
  2. Empty Select: An empty select select {} will block forever.
  3. Sending: select also works for sending: case c1 <- "data":. This will run only if c1 is ready to accept data.