ZetCode

Golang select keyword

last modified May 7, 2025

This tutorial explains how to use the select keyword in Go. We'll cover channel operations basics with practical examples of concurrent patterns.

The select statement lets a goroutine wait on multiple communication operations. It chooses one ready case randomly if multiple are available.

In Go, select is essential for coordinating between goroutines. It handles channel sends/receives and implements timeouts and non-blocking ops.

Basic select with two channels

This example shows the simplest use of select with two channels. It waits for data from either channel.

basic_select.go
package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- "from ch1"
    }()

    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "from ch2"
    }()

    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println(msg1)
        case msg2 := <-ch2:
            fmt.Println(msg2)
        }
    }
}

The select waits for either channel to have data. Since ch1 is faster, its message prints first. The loop ensures both messages are received.

Select with default case

Adding a default case makes select non-blocking. This example demonstrates immediate execution when no channels are ready.

default_select.go
package main

import "fmt"

func main() {
    ch := make(chan string)

    select {
    case msg := <-ch:
        fmt.Println("Received:", msg)
    default:
        fmt.Println("No message received")
    }
}

Since the channel is empty and no goroutine is sending, the default case executes immediately. This pattern is useful for polling channels.

Select with timeout

Using time.After in select implements timeouts. This prevents indefinite blocking on channel operations.

timeout_select.go
package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan string)

    go func() {
        time.Sleep(3 * time.Second)
        ch <- "result"
    }()

    select {
    case res := <-ch:
        fmt.Println(res)
    case <-time.After(2 * time.Second):
        fmt.Println("timeout")
    }
}

The timeout case triggers after 2 seconds, while the goroutine takes 3 seconds. This ensures the program doesn't wait indefinitely for slow operations.

Select for sending and receiving

Select can handle both send and receive operations. This example demonstrates coordinating between different channel directions.

send_receive_select.go
package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan int)
    done := make(chan bool)

    go func() {
        for {
            select {
            case ch <- 1:
                fmt.Println("Sent 1")
            case <-done:
                fmt.Println("Stopping")
                return
            }
        }
    }()

    go func() {
        time.Sleep(2 * time.Second)
        done <- true
    }()

    for i := 0; i < 5; i++ {
        fmt.Println("Received:", <-ch)
        time.Sleep(300 * time.Millisecond)
    }
}

The first goroutine alternates between sending values and checking the done channel. After 2 seconds, the done channel stops the sender.

Select with multiple ready cases

When multiple cases are ready, select picks one randomly. This example shows the non-deterministic behavior.

random_select.go
package main

import "fmt"

func main() {
    ch1 := make(chan string, 1)
    ch2 := make(chan string, 1)

    ch1 <- "one"
    ch2 <- "two"

    for i := 0; i < 10; i++ {
        select {
        case msg := <-ch1:
            fmt.Println(msg)
            ch1 <- "one"
        case msg := <-ch2:
            fmt.Println(msg)
            ch2 <- "two"
        }
    }
}

Both channels are ready, so select chooses randomly between them. The output shows a mix of "one" and "two" in unpredictable order.

Select with nil channels

Select ignores nil channels. This example demonstrates using nil to disable cases dynamically.

nil_select.go
package main

import (
    "fmt"
    "time"
)

func main() {
    var ch chan string

    go func() {
        time.Sleep(2 * time.Second)
        ch = make(chan string, 1)
        ch <- "message"
    }()

    for {
        select {
        case msg := <-ch:
            fmt.Println("Received:", msg)
            return
        default:
            fmt.Println("No channel yet")
            time.Sleep(500 * time.Millisecond)
        }
    }
}

Initially ch is nil, so only the default case executes. After 2 seconds, ch becomes active and can receive the message.

Practical example: Worker pool

This practical example shows using select in a worker pool pattern to coordinate multiple goroutines.

worker_select.go
package main

import (
    "fmt"
    "time"
)

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        fmt.Printf("worker %d started job %d\n", id, j)
        time.Sleep(time.Second)
        fmt.Printf("worker %d finished job %d\n", id, j)
        results <- j * 2
    }
}

func main() {
    jobs := make(chan int, 100)
    results := make(chan int, 100)

    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }

    for j := 1; j <= 5; j++ {
        jobs <- j
    }
    close(jobs)

    for a := 1; a <= 5; a++ {
        select {
        case res := <-results:
            fmt.Println("result:", res)
        case <-time.After(2 * time.Second):
            fmt.Println("timeout waiting for result")
        }
    }
}

The select waits for worker results with a timeout. It ensures the program doesn't hang if a worker fails to complete its task.

Source

Go language specification

This tutorial covered the select keyword in Go with practical examples of channel operations in concurrent programming.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Golang tutorials.