Go language review: use select to solve the problem of blocking in the channel
problem background
In Go language, when we read data from an unclosed channel, the read operation blocks if there is no data in the channel. The traditional practice is to traverse the channel until it is closed, but in actual development, we may not be able to determine when the channel should be turned off, or when multiple goroutines write data to the channel at the same time, the shutdown timing is difficult to grasp.
If not handled properly, it will lead to DeadLock (deadlock).
Solution: SELECT statement
The SELECT statement allows the program to wait for multiple channel operations at the same time, and when any one case can be executed, the case will be executed. With the default branch, non-blocking channel reading can be realized.
Code example
Let’s look at a specific example:
package main
import (
"fmt"
"time"
)
func main() {
// 1. 定义一个容量为 10 的 int 类型 channel
intChan := make(chan int, 10)
for i := 0; i < 10; i++ {
intChan <- i
}
// 2. 定义一个容量为 5 的 string 类型 channel
stringChan := make(chan string, 5)
for i := 0; i < 5; i++ {
stringChan <- "hello" + fmt.Sprintf("%d", i)
}
// 使用 select 循环从多个 channel 读取数据
for {
select {
case v := <-intChan:
fmt.Printf("从 intChan 读取的数据: %d\n", v)
time.Sleep(time.Second)
case v := <-stringChan:
fmt.Printf("从 stringChan 读取的数据: %s\n", v)
time.Sleep(time.Second)
default:
fmt.Printf("都取不到了,退出程序\n")
time.Sleep(time.Second)
return
}
}
}
Running result: as shown in Figure 1

/app/go-atguigu/channel-details # go run main.go
从 stringChan 读取的数据: hello0
从 stringChan 读取的数据: hello1
从 stringChan 读取的数据: hello2
从 stringChan 读取的数据: hello3
从 intChan 读取的数据: 0
从 intChan 读取的数据: 1
从 intChan 读取的数据: 2
从 stringChan 读取的数据: hello4
从 intChan 读取的数据: 3
从 intChan 读取的数据: 4
从 intChan 读取的数据: 5
从 intChan 读取的数据: 6
从 intChan 读取的数据: 7
从 intChan 读取的数据: 8
从 intChan 读取的数据: 9
都取不到了,退出程序
/app/go-atguigu/channel-details #
Code resolution
1. Create a buffered channel
We created two buffered channels:
– `IntChan`: capacity is 10, write 10 integers
`StringChan`: capacity is 5, write 5 strings
Since it is a buffered channel, the write operation will not block until the buffer is full.
2. Select multiplex
The core for-select structure:
for {
select {
case v := <-intChan:
// 处理 intChan 的数据
case v := <-stringChan:
// 处理 stringChan 的数据
default:
// 两个 channel 都没有数据时执行
return
}
}
Key points:
– Select randomly selects an executable case
– If multiple cases are executable, randomly select one
– If there is no case executable and there is a default branch, execute default immediately
– If there is no case executable and no default, select will block
3. The role of the default branch
The default branch is the key to solving the blocking problem:
– When both channels have no data, there is no blocking waiting
– instead execute the default branch immediately
– In Default we can decide to exit the loop or perform other logic
This avoids a deadlock caused by waiting for a channel that will never have a signal to turn off.
Contrast with traditional
Problems with the traditional way:
// 需要明确关闭 channel
close(intChan)
for v := range intChan {
fmt.Println(v)
}
This method requires us to know when to close the channel, which may be more complicated in multi-goroutine scenarios.
Advantages of the Select method:
– No need to explicitly close the channel
– Multiple channels can be monitored at the same time
– Implementing non-blocking reads via default
– More flexible control flow
Precautions
1. Performance considerations: Sleep is 1 second after each read in the example, and it should be adjusted according to business needs in practical applications
2. Fairness: Select is randomly selected when multiple cases are available, and fairness is not guaranteed
3. Resource release: Although it is not necessary to close the channel to avoid deadlock, it is still a good practice to close the channel at the appropriate time, so that the receiver can know that there will be no new data
4. Break tag: If you need to jump out of the outer loop from the select, you can use the label:
label:
for {
select {
case v := <-ch:
if condition {
break label
}
}
}
Summary
The SELECT statement is an important tool in Go language concurrent programming, which provides:
– Multi-channel monitoring at the same time
– Non-blocking channel operations (via default)
– Elegant timeout control
– Flexible concurrency process management
In actual development, rational use of select can make our concurrent code more robust and easy to maintain. Especially when dealing with multiple data sources, implementing timeout mechanisms, or avoiding deadlock scenarios, select is a very practical tool.
I hope this review can help you better understand and apply the SELECT mechanism of Go language!