模块 7 · 第 3 课(共 5)课程中的33/50~12 min
模块课程(3/5)
`select`:多路复用通道
select 同时等待多个通道操作。
它是 Go 并发的 switch :它让你对第一个做出反应
许多人之间的渠道已准备就绪。
句法
Go
select {
case v := <-ch1:
fmt.Println("ch1:", v)
case v := <-ch2:
fmt.Println("ch2:", v)
case ch3 <- 99:
fmt.Println("inviato su ch3")
case <-time.After(time.Second):
fmt.Println("timeout")
default:
fmt.Println("nessuno è pronto")
}语义:
- 对所有案例进行评估; 仅选择一个已准备好的案例。
- 如果准备好多个案例,则选择是伪随机(无顺序)。
- 如果没有一个箱子准备好:
select会阻塞,直到有一个箱子准备好为止。 - 如果有
default:当没有准备好案例时它立即运行(非阻塞选择)。
超时模式
Go
select {
case res := <-fetch(url):
use(res)
case <-time.After(2 * time.Second):
log.Println("timeout: fetch troppo lento")
}time.After(d) 返回一个 <-chan Time,它在 d 之后生成一个值。
这是减少等待时间的经典方法。
默认非阻塞选择
Go
select {
case msg := <-ch:
process(msg)
default:
// niente da fare, prosegui
}它“轮询”通道而不阻塞。在代码路径中很有用 必须保持响应而不强制等待。
“完成通道”模式
将 select 与取消通道结合起来:
Go
func worker(in <-chan Job, done <-chan struct{}) {
for {
select {
case j := <-in:
process(j)
case <-done:
return // chi gestisce done segnala lo shutdown
}
}
}来自协调器的 close(done) 在每个触发器中触发 case <-done
等待 goroutine:协调关闭。
循环选择
Go
for {
select {
case v := <-input:
if v == nil {
return
}
handle(v)
case <-time.After(5 * time.Second):
keepAlive()
case <-ctx.Done():
return
}
}服务器和工作人员的典型“事件循环”模式。
零通道作为“禁用”情况
高级技巧:带有 ch == nil 的 case <-ch 永远不会
已选择。它允许您动态“禁用”案例:
Go
var in chan int = source()
for {
select {
case v, ok := <-in:
if !ok {
in = nil // disabilita questo case
continue
}
handle(v)
case <-ctx.Done():
return
}
}当 in 关闭时,我将其置空: select 只继续处理
其他情况。
尝试一下
锻炼#go.m7.l3.e1
尝试:0加载中...
使用从 ch1 和 ch2 接收的两种情况实现选择;打印哪一个最先到达。
正在加载编辑器...
显示提示
语法 `select { case ...: ... }`,每个通道一种情况。
3 次尝试后可用的解决方案
锻炼#go.m7.l3.e2
尝试:0加载中...
使用 time.After(time.Second) 将超时情况添加到选择。
正在加载编辑器...
显示提示
`time.After(d)` 是在 d 之后接收的通道。
3 次尝试后可用的解决方案
Quiz#go.m7.l3.e3
准备好是什么让选择成为非阻塞的?
Go
select { case v := <-ch: ... ??? }回顾
select等待多个通道操作中的第一个就绪情况。- 多个就绪案例之间的伪随机选择。
default→ 非阻塞选择(轮询)。time.After(d)用于超时(注意循环中的泄漏:使用context)。- 模式:完成通道、事件循环、禁用案例的零通道。
- 空
select{}= 永恒块:几乎总是一个错误。