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.