跳转到主要内容
模块 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{} = 永恒块:几乎总是一个错误。