Introduction
The select
statement in Go allows a goroutine to wait on multiple communication operations, such as receiving from or sending to channels. It blocks until one of its cases can proceed, making it used for handling multiple channels and implementing timeouts, multiplexing, and non-blocking communication. In this chapter, you will learn the basics of using select
in Go, including examples of common use cases.
Basic Usage
The select
statement looks similar to a switch
statement but operates on channels.
Example: Basic Select
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(1 * time.Second)
ch1 <- "one"
}()
go func() {
time.Sleep(2 * time.Second)
ch2 <- "two"
}()
for i := 0; i < 2; i++ {
select {
case msg1 := <-ch1:
fmt.Println("Received", msg1)
case msg2 := <-ch2:
fmt.Println("Received", msg2)
}
}
}
In this example, the select
statement waits for messages from either ch1
or ch2
. It prints the message from the channel that receives first.
Implementing Timeouts
The select
statement can be used to implement timeouts by using the time.After
function, which returns a channel that sends the current time after a specified duration.
Example: Timeout
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan string)
go func() {
time.Sleep(2 * time.Second)
ch <- "result"
}()
select {
case res := <-ch:
fmt.Println("Received", res)
case <-time.After(1 * time.Second):
fmt.Println("Timeout")
}
}
In this example, the select
statement waits for either the result from ch
or a timeout after 1 second. Since the result takes 2 seconds, the timeout case is executed.
Non-Blocking Communication
The select
statement can be used for non-blocking communication by using the default case.
Example: Non-Blocking Send and Receive
package main
import (
"fmt"
)
func main() {
ch := make(chan string)
select {
case msg := <-ch:
fmt.Println("Received", msg)
default:
fmt.Println("No message received")
}
msg := "hello"
select {
case ch <- msg:
fmt.Println("Sent", msg)
default:
fmt.Println("No message sent")
}
}
In this example, the first select
statement tries to receive from ch
but falls through to the default case because ch
is empty. The second select
statement tries to send a message to ch
but falls through to the default case because there is no receiver.
Multiplexing Channels
The select
statement can be used to multiplex multiple channels, allowing a goroutine to wait on multiple communication operations simultaneously.
Example: Multiplexing Channels
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, 5)
results := make(chan int, 5)
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)
}
}
}
In this example, multiple worker goroutines receive jobs from the jobs
channel and send results to the results
channel. The main goroutine waits for results using a select
statement.
Conclusion
The select
statement in Go provides a powerful way to handle multiple channels and implement complex communication patterns. By understanding how to use select
for basic operations, timeouts, non-blocking communication, and multiplexing, you can write more robust and efficient concurrent programs in Go.