24 Select

64 阅读2分钟

原文地址

What is select?

  • The select statement 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 switch except 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