select 多路复用
一个 goroutine 同时监听多个 channel 的「事件循环」:C++/epoll 监听多个 fd 的那个心智模型,Go 把它内建成了 select 语句 一、简述 select 是 Go 语法层面的多路复用(multiplexing)原语:它让一个 goroutine 同时等待多个 channel 上的收发操作,只要其中一个就绪,就走对应的分支。 对于 C++ 开发者,最贴切的类比是 Linux epoll / poll:你不想为每个 socket 各开一个线程去阻塞读,而是用一个线程同时监听一堆 fd,哪个有事件处理哪个。select 就是「 goroutine 版本的 epoll」——但它更进一步:它还能同时做非阻塞尝试(default)、定时超时(time.After)、动态禁用分支(nil channel)。 核心要点:select 是「组合 channel 为复杂同步逻辑」的胶水。没有 select,一个 goroutine 只能死等一个 channel;有了 select,你才能写超时、写取消、写事件循环、写多路汇流。 二、C++ 对照速查表 概念 C++ Go 关键差异 多路监听 epoll_wait(fds, ...) / poll() select { case <-ch1: ... } Go 是对 channel 通道复用,C++ 是对 fd 复用 同时等两个事件 一个线程一个 fd 各睡一觉 / epoll 一个 goroutine 同时等多个 case select 内建,代码直白得多 随机公平选择 epoll 按就绪顺序返回 多 case 就绪时随机选一个 Go 保证公平:随机而非先后 非阻塞尝试 poll(fd, timeout=0) select { case ...: default: } default = 都不就绪立即走 超时等待 epoll_wait 的 timeout 参数 select + time.After(1s) 超时是普通 channel,不是特殊机制 收发都监听 读 / 写事件分别注册 一个 select 里既有发送又有接收 case 语法上收发对称 监听无限器 服务端 accept 线程循环 for { select { ... } } 事件循环惯用法 特定事件暂时屏蔽 取消注册 fd case 的 channel 置 nil → 永久阻塞 nil channel 妙用 三、逐主题详解 3.1 select 基本语法 package main import ( "fmt" "time" ) func main() { ch1 := make(chan string) ch2 := make(chan string) // 两个 goroutine 各自往不同 channel 发 go func() { time.Sleep(100 * time.Millisecond); ch1 <- "来自 ch1" }() go func() { time.Sleep(300 * time.Millisecond); ch2 <- "来自 ch2" }() // select 会阻塞,直到某个 case 就绪 select { case msg := <-ch1: fmt.Println("收到:", msg) // 大概率先到 ch1(100ms 更快) case msg := <-ch2: fmt.Println("收到:", msg) } } 要点: ...
豫公网安备41108202000982号