select 语句
本课属于 Coddy GO 学习旅程的“面向对象编程”单元。第 62 课,共 107 课。
select 语句可以让 goroutine 同时等待多个通道操作。它类似于 switch 语句,但每个 case 都涉及通道发送或接收。当多个通道都准备就绪时,select 会随机选择一个。
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() { ch1 <- "from channel 1" }()
go func() { ch2 <- "from channel 2" }()
select {
case msg1 := <-ch1:
fmt.Println(msg1)
case msg2 := <-ch2:
fmt.Println(msg2)
}
}如果不使用 select,从 ch1 接收数据时会一直阻塞,直到数据到达,这可能会错过 ch2 上的数据。使用 select 后,哪个通道先收到数据,就先处理哪个通道。
default 分支让 select 不会阻塞。如果没有就绪的通道,它会立即执行:
select {
case msg := <-messages:
fmt.Println("Received:", msg)
default:
fmt.Println("No message available")
}一种常见模式是将 select 与使用 time.After 的超时机制结合起来。它会返回一个通道,并在指定的时间间隔后接收一个值:
select {
case result := <-ch:
fmt.Println("Got result:", result)
case <-time.After(2 * time.Second):
fmt.Println("Timeout!")
}这样可以避免通道操作可能永远无法完成时,你的程序一直等待。
挑战
简单让我们构建一个服务监控器,使用 select 语句处理多个并发数据源。监控器会同时监听不同的服务通道,并在收到数据或没有可用更新时做出相应响应。
你将在两个文件中编写代码:
monitor.go:定义服务监控逻辑,并使用基于 select 的通道处理方式。创建一个
ServiceUpdate结构体,包含Name(字符串)和Status(字符串)字段。实现一个
Monitor函数,接收三个通道:primary chan ServiceUpdate、backup chan ServiceUpdate和done chan bool。此函数应使用select同时监听这三个通道:- 从
primary接收时:返回格式化字符串Primary: [Name] is [Status] - 从
backup接收时:返回格式化字符串Backup: [Name] is [Status] - 从
done接收时:返回Monitoring stopped
实现一个
CheckStatus函数,接收一个updates chan ServiceUpdate,并使用带有default分支的select进行非阻塞检查:- 如果有可用更新:返回
Update available: [Name] - 如果没有可用更新(default 分支):返回
No updates pending
- 从
main.go:设置通道,并演示阻塞和非阻塞的 select 行为。从输入中读取一个模式,用来决定运行哪种情形:
primary、backup、stop或check。对于
primary、backup和stop模式:还要读取服务名称和状态。创建三个通道,启动一个 goroutine,根据模式向相应的通道发送数据,然后调用Monitor并打印其结果。对于
check模式:读取服务名称和状态,以及一个标志(send或nosend),用于指示检查前是否发送数据。创建一个容量为 1 的缓冲通道。如果标志是send,就在调用CheckStatus前向通道发送一条更新。打印CheckStatus的结果。
输入内容如下:
- 第 1 行:模式(
primary、backup、stop或check) - 第 2 行:服务名称
- 第 3 行:服务状态
- 第 4 行(仅用于
check模式):send或nosend
例如,给定:
primary
Database
healthy输出应为:
Primary: Database is healthy再例如,给定:
check
Cache
active
nosend输出应为:
No updates pending亲手试试
package main
import (
"fmt"
)
func main() {
var mode string
fmt.Scanln(&mode)
var serviceName string
fmt.Scanln(&serviceName)
var serviceStatus string
fmt.Scanln(&serviceStatus)
// TODO:处理不同模式:"primary"、"backup"、"stop"、"check"
// 对于 "primary"、"backup"、"stop" 模式:
// 创建 3 个通道:primary、backup、done
// 启动一个 goroutine,向对应的通道发送数据
// 调用 Monitor 并打印结果
// 对于 "check" 模式:
// 读取额外的标志("send" 或 "nosend")
// 创建一个容量为 1 的缓冲通道
// 如果标志是 "send",就向通道发送更新
// 调用 CheckStatus 并打印结果
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Go 在线编译器