Today, let’s take a closer look at the usage of SELECT in Go language. It is similar to switch case, with a single usage. It can only be used for channel/channel related operations, and each case corresponds to a channel communication (receiving or sending) process. Select waits until a case communication completes, and then executes the statement corresponding to the case branch

// The syntax is as follows:
select{
    case <-ch1:
        ...
    case data := <-ch2:
        ...
    case ch3<-data:
        ...
    default: Default action}Copy the code

Next, let’s look at a few examples to help understand the select model.

1. The simplest example

package main

import (
    "fmt"
)

func main(a) {
    c1 := make(chan string.1)
    c2 := make(chan string.1)

    c2 <- "hello"

    select {
    case msg1 := <-c1:
      fmt.Println("c1 received: ", msg1)
    case msg2 := <-c2:
      fmt.Println("c2 received: ", msg2)
    default:
      fmt.Println("No data received.")}}Copy the code

C2 received: hello c2 received: hello c2 received: hello

2. Avoid deadlock

Select must hit one of the branches during execution. If all cases are iterated and no case expression is hit, the code branch in default is entered.

But what happens when you don’t write the default branch? The SELECT will then block until a case is hit, and if no hit is made, the SELECT will throw an error in the deadlock, as follows.

package main
import (
    "fmt"
)
func main(a) {
    c1 := make(chan string.1)
    c2 := make(chan string.1)
    // c2 <- "hello"
    select {
    case msg1 := <-c1:
        fmt.Println("c1 received: ", msg1)
    case msg2 := <-c2:
        fmt.Println("c2 received: ", msg2)
        // default:
        // fmt.Println("No data received.")}}Copy the code

The output is as follows

fatal error: all goroutines are asleep - deadlock!
goroutine 1 [select]:
nginxlog/tools.OutPrintf()
	D:/work/perlearn/go_space/total/tools/parasenginx.go:233 +0xc5
main.main()
	D:/work/perlearn/go_space/total/main.go:8 +0x17
Process finished with exit code 2
Copy the code

3,There are two ways to solve this problem

One is to get into the habit of writing the default branch when you select, even though you’re not writing any code under default. At this point, the above code will not work

package main

import (
    "fmt"
)
func main(a) {
    c1 := make(chan string.1)
    c2 := make(chan string.1)
  // c2 <- "hello"
    select {
    case msg1 := <-c1:
        fmt.Println("c1 received: ", msg1)
    case msg2 := <-c2:
        fmt.Println("c2 received: ", msg2)
    default:}}Copy the code

The other is to make sure that one of the channels can receive data

package main

import (
    "fmt"
    "time"
)
func main(a) {
    c1 := make(chan string.1)
    c2 := make(chan string.1)
  // Start a coroutine that can send data to the channel
    go func(a) {
        time.Sleep(time.Second * 1)
        c2 <- "hello"} ()select {
    case msg1 := <-c1:
        fmt.Println("c1 received: ", msg1)
    case msg2 := <-c2:
        fmt.Println("c2 received: ", msg2)
    }
}
Copy the code

4, select randomness

In switch, cases are executed sequentially, but not in SELECT.

func main(a){
    c1 := make(chan string.1)
	c2 := make(chan string.1)
	c1 <- "hello"
	c2 <- "123123"
	select {
	case msg1 := <-c1:
		fmt.Println("c1 received: ", msg1)
	case msg2 := <-c2:
		fmt.Println("c2 received: ", msg2)
	}
}
Copy the code

5. Select timeout

When the channel in the case never receives data and there is no default statement, the select will block as a whole, but sometimes we do not want the select to block forever, so we can manually set a timeout

func main(a){
	ch1:=make(chan string.1)
	ch2:=make(chan string.1)
	timeout:=make(chan bool.1)
	go func(ch chan bool, t int) {
		time.Sleep(time.Second * time.Duration(t))
		ch <- true
	}(timeout,2)
	select {
	case msg:=<-ch1:
		fmt.Println("ch1 recvied data",msg)
	case ms2:=<-ch2:
		fmt.Println("ch2 recvied data",ms2)
	case <-timeout:
		fmt.Println("Timeout, exit.")}}Copy the code

The output is as follows

Timeout, exit.
Copy the code

6. To sum up

Select is similar to Switch, but it is used in more specific scenarios. Here are a few differences you need to know as you follow this article:

  • Select can only be used for channel operations (write/read), whereas switch is more general;
  • Select cases are random, whereas switch cases are sequential;
  • Select to avoid deadlock, but also can implement their own timeout mechanism;
  • There is no use of switch fallthrough in select;
  • Select cannot be followed by functions or other expressions like switch.