What is select?
- The
selectstatement is used to choose from multiple send/receive channel operations. - The select statement blocks until one of the send/receive operations is ready.
- If multiple operations are ready, one of them is chosen at random.
- The syntax is similar to
switchexcept that each of the case statements will be a channel operation.
package main
import (
"fmt"
"time"
)
func server1(ch chan string) {
time.Sleep(6 * time.Second)
ch <- "from server1"
}
func server2(ch chan string) {
time.Sleep(3 * time.Second)
ch <- "from server2"
}
func main() {
output1 := make(chan string)
output2 := make(chan string)
go server1(output1)
go server2(output2)
select {
case s1 := <-output1:
fmt.Println(s1)
case s2 := <-output2:
fmt.Println(s2) // will be invoked. After three secs.
}
}
Practical use of select
package main
import (
"fmt"
"time"
)
func process(ch chan string) {
time.Sleep(10500 * time.Millisecond)
ch <- "process successful"
}
func main() {
ch := make(chan string)
go process(ch)
for {
time.Sleep(1000 * time.Millisecond)
select {
case v := <-ch:
fmt.Println("received value: ", v)
return
default:
fmt.Println("no value received")
}
}
}
no value received
no value received
no value received
no value received
no value received
no value received
no value received
no value received
no value received
no value received
received value: process successful
Deadlock and default case
package main
func main() {
ch := make(chan string)
select {
case <-ch:
}
}
The select statement will block forever since no other Goroutine is writing to this channel and hence will result in deadlock.
This program will panic at runtime with the following message
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
/tmp/sandbox627739431/prog.go:6 +0x4d
If a default case is present, this deadlock will not happen.
package main
import "fmt"
func main() {
ch := make(chan string)
select {
case <-ch:
default:
fmt.Println("default case executed")
}
}
Random selection
When multiple cases in a select statement are ready, one of them will be executed at random.
package main
import (
"fmt"
"time"
)
func server1(ch chan string) {
ch <- "from server1"
}
func server2(ch chan string) {
ch <- "from server2"
}
func main() {
output1 := make(chan string)
output2 := make(chan string)
go server1(output1)
go server2(output2)
time.Sleep(1 * time.Second)
select {
case s1 := <-output1:
fmt.Println(s1)
case s2 := <-output2:
fmt.Println(s2)
}
}
Gotcha - Empty select
package main
func main() {
select {}
}
We know that the select statement will block until one of its cases is executed. In this case, the select statement doesn’t have any cases and hence it will block forever resulting in a deadlock. This program will panic with the following output,
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [select (no cases)]:
main.main()
/tmp/sandbox246983342/prog.go:4 +0x25