Detailed explanation of select multiple selection in golang development

Time:2021-4-18

Select is a control structure in golang, which is syntactically similar to switch statement, except that select is used for communication between goroutines. Each case must be a communication operation, either sending or receiving. Select will randomly execute a runnable case. If there is no case to run, goroutine will block until there is a case to run.

Select multiple selection

The writing method of select is basically the same as that of switch case, except that select of golang is a communication control statement. The execution of select must have communication sending or receiving. If not, it will be blocked all the time.


ch := make(chan bool, 0)
	ch1 := make(chan bool, 0)
	select {
		case ret := <-ch:
			fmt.Println(ret)
		case ret := <-ch1:
			fmt.Println(ret)
	}

If neither ch nor ch1 sends communication data, select will block until ch or ch1 sends data, and select will execute corresponding case to receive data.

Select to realize timeout control

We can use the select mechanism to implement a simple timeout control.
First look at the complete implementation of the program code


func service(ch chan bool) {
	time.Sleep(time.Second*3)
	ch<-true
}
func main() {
	ch := make(chan bool, 0)
	go service(ch)
	select {
		case ret := <-ch:
			fmt.Println(ret)
		case <-time.After(time.Second*5):
			fmt.Println("timeout")
	}
}

___go_build_main_go #gosetup
true

You can see the use of time.After Timeout defines 5S, and the service program executes 3S, so there must be no timeout, which is consistent with the expectation.
Let’s take a look at the execution of timeout. We will set the execution time of service program to 6S. Timeout control continues to be 5S, and then look at the implementation effect


func service(ch chan bool) {
	time.Sleep(time.Second*6)
	ch<-true
}
func main() {
	ch := make(chan bool, 0)
	go service(ch)
	select {
		case ret := <-ch:
			fmt.Println(ret)
		case <-time.After(time.Second*5):
			fmt.Println("timeout")
	}
}

___go_build_main_go #gosetup
timeout

When the timeout case is executed, it is consistent with the expectation.

Select to determine whether the channel is closed

Let’s first look at the syntax of receiving data

val,ok <- ch
OK, receive data normally
OK false channel closed

You can see that there are actually two parameters to accept data. The second bool value will reflect whether the channel is closed and whether the data can be accepted normally.

Take a look at the test code
We have written a data sender and two data receivers. When the sender closes the channel, the goroutines of the two receivers can judge whether the channel is closed through the above syntax and decide whether their goroutines end.


func sender(ch chan int, wg *sync.WaitGroup) {
	for i:=0;i<10;i++ {
		ch<-i
	}
	close(ch)
	wg.Done()
}
func receiver(ch chan int, wg *sync.WaitGroup) {
	for {
		if val,ok := <-ch;ok {
			fmt.Println(fmt.Sprintf("%d,%s",val, "revevier"))
		} else {
			fmt.Println("quit recevier")
			break;
		}
	}
	wg.Done()
}
func receiver2(ch chan int, wg *sync.WaitGroup) {
	for {
		if val,ok := <-ch;ok {
			fmt.Println(fmt.Sprintf("%d,%s",val, "revevier2"))
		} else {
			fmt.Println("quit recevier2")
			break;
		}
	}
	wg.Done()
}
func main() {
	ch := make(chan int, 0)
	wg := &sync.WaitGroup{}
	wg.Add(1)
	go sender(ch, wg)
	wg.Add(1)
	go receiver(ch, wg)
	wg.Add(1)
	go receiver2(ch, wg)
	wg.Wait()
}

results of enforcement

0,revevier2
2,revevier2
3,revevier2
4,revevier2
5,revevier2
6,revevier2
7,revevier2
1,revevier
9,revevier
quit recevier
8,revevier2
quit recevier2

You can see that there is a data sender and two data receivers. When the channel is closed, both data receivers receive the notification of channel closing.
It should be noted that when sending data to a closed channel, the program will panic and receive data from a closed channel. It will receive zero value data of channel type with no reference meaning. Int is 0 and string is empty

Select exit timer and other programs

The rotation timer is often used in development, but when the program exits, the rotation timer cannot be closed. In fact, select can solve this problem.
If we have a rotation task, we need a timer to execute logic every 3S and close the timer after 10s.

Look at the code


func TimeTick(wg *sync.WaitGroup,q chan bool) {
	defer wg.Done()
	t := time.NewTicker(time.Second*3)
	defer t.Stop()
	for {
		select {
		case <-q:
			fmt.Println("quit")
			return
		case <-t.C:
			fmt.Println("seconds timer")
		}
	}
}
func main() {
	q := make(chan bool)
	wg := new(sync.WaitGroup)
	wg.Add(1)
	go TimeTick(wg,q)
	time.Sleep(time.Second*10)
	close(q)
	wg.Wait()
}

results of enforcement

seconds timer
seconds timer
seconds timer
quit

It’s very elegant to exit the rotation timer goroutine by closing the channel,

So far, this article about select multi-channel selection in golang development is introduced here. For more information about golang select multi-channel selection, please search previous articles of developer or continue to browse the following related articles. I hope you can support developer more in the future!