Go 并发编程基础:select 多路复用

select 是 Go 并发编程中非常强大的语法结构,它允许程序同时等待多个通道操作 的完成,从而实现多路复用机制,是协程调度、超时控制、通道竞争等场景的核心工具。


一、什么是 select

select 类似于 switch 语句,但它用于监听多个通道的发送/接收操作 。一旦其中任意一个通道准备就绪,select 就会执行相应的语句块。

基本语法:

go 复制代码
select {
case val := <-ch1:
    // ch1 可读时执行
case ch2 <- data:
    // ch2 可写时执行
default:
    // 所有通道都不准备好时执行(可选)
}

二、select 使用示例

示例1:监听多个通道输入

go 复制代码
func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- "消息来自 ch1"
    }()

    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "消息来自 ch2"
    }()

    select {
    case msg1 := <-ch1:
        fmt.Println(msg1)
    case msg2 := <-ch2:
        fmt.Println(msg2)
    }
}

输出(大概率):

复制代码
消息来自 ch1

谁先准备好,谁被执行。


三、select 的特性

  • • 随机选择 :如果多个 case 同时满足,Go 会随机选择一个执行,避免饥饿。
  • • 阻塞行为 :当没有任何 case 可以运行时,select 会阻塞,除非有 default。
  • • 可配合 for 使用:实现多路轮询、协程调度等高级用法。

四、使用 select 实现超时机制

select 搭配 time.After() 可实现通道的超时控制:

css 复制代码
select {
case msg := <-ch:
    fmt.Println("收到消息:", msg)
case <-time.After(2 * time.Second):
    fmt.Println("超时未收到")
}

实用场景:

  • • 网络请求超时
  • • 等待任务执行完成
  • • 控制并发阻塞时间

五、非阻塞通信:使用 default

go 复制代码
select {
case msg := <-ch:
    fmt.Println("收到:", msg)
default:
    fmt.Println("没有收到任何数据")
}

不等待,立即返回默认分支。


六、监听通道关闭

配合 range 和 select,可以优雅处理通道关闭:

go 复制代码
for {
    select {
    case msg, ok := <-ch:
        if !ok {
            fmt.Println("通道已关闭")
            return
        }
        fmt.Println("收到:", msg)
    }
}

七、实践:合并多个输入通道

go 复制代码
func merge(ch1, ch2 <-chan string) <-chan string {
    out := make(chan string)
    go func() {
        for {
            select {
            case msg := <-ch1:
                out <- msg
            case msg := <-ch2:
                out <- msg
            }
        }
    }()
    return out
}

✅ 实现"扇入"(fan-in)模式,将多个输入流合并成一个输出。


八、小结

功能 是否支持
同时监听多个通道 ✅
随机选择就绪的通道执行 ✅
支持默认分支防止阻塞 ✅
可实现超时控制与轮询 ✅
实现非阻塞收发或关闭判断 ✅

实战建议

  • • 为所有关键的通道通信加上 select 和超时控制,避免协程泄漏。
  • • 避免使用 select 轮询空通道导致死循环。
  • • 多通道合并、拆分时,配合 select 和 sync.WaitGroup 效果更佳。

相关推荐
子兮曰3 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰3 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
爱勇宝3 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
胡写代码3 天前
别再前后端各写一套表单校验了
java·后端
大勇前进3 天前
原生 PHP 还是 Laravel?小项目到底要不要上框架
后端
yuzhi_liu3 天前
我用 LangGraph4j 实现 Multi-Agent Supervisor
后端
alsmile3 天前
Node-RED 之外,国产规则引擎的新方案:基于标准语法,Go 先行实现
后端·开源·go
大白803 天前
PHP 内存溢出排查思路:看懂报错日志,精准定位问题
后端
二月龙3 天前
PHP 接口返回统一响应封装,让前后端对接更省心
后端
盖伦发发3 天前
软件工程SOLID 五大设计原则
后端·软件工程