在 Go 语言中,select {}
是一个永久阻塞当前 goroutine的空语句(不操作任何 channel),而 select
与 channel
结合使用时可以实现多路复用或非阻塞通信。以下是详细说明和典型用法:
1. 空 select {}
- 行为:直接阻塞,不监听任何 channel,通常用于阻止程序退出。
- 示例:
select {} // goroutine 永久挂起
2. select
+ channel 的常见模式
(1) 多路 channel 监听
ch1 := make(chan int)
ch2 := make(chan string)
go func() {
for {
select {
case num := <-ch1:
fmt.Println("Received from ch1:", num)
case str := <-ch2:
fmt.Println("Received from ch2:", str)
}
}
}()
- 特点:同时监听多个 channel,哪个就绪就执行哪个 case。
(2) 非阻塞 channel 操作
通过 default
实现非阻塞检查:
select {
case val := <-ch:
fmt.Println("Received:", val)
default:
fmt.Println("No data in channel")
}
(3) 超时控制
结合 time.After
实现超时:
select {
case res := <-longOperation():
fmt.Println(res)
case <-time.After(3 * time.Second):
fmt.Println("Timeout!")
}
3. select
的行为规则
- 随机选择:如果多个 case 同时就绪,随机执行一个。
- 阻塞等待:没有 default 且无就绪 case 时,阻塞当前 goroutine。
- default:立即执行(当其他 case 未就绪时)。
4. ⚠️注意事项
- 资源泄漏:长期阻塞的 goroutine(如
select{}
)需确保有退出机制。 - 关闭 channel:已关闭的 channel 会立即返回零值,可通过第二个返回值判断:
select { case v, ok := <-ch: if !ok { fmt.Println("Channel closed!") } }
5. vs for-range channel
for range ch |
select + case <-ch |
|
---|---|---|
用途 | 单一 channel 消费 | 多路监听/非阻塞操作 |
退出 | channel关闭后自动退出 | 需手动处理关闭信号 |
6.完整示例
func worker(stopCh chan struct{}) {
for {
select {
case <-stopCh: //收到退出信号
fmt.Println("Exiting...")
return
default:
fmt.Println("Working...")
time.Sleep(1 * time.Second)
}
}
}
func main() {
stopCh := make(chan struct{})
go worker(stopCh)
time.Sleep(3 * time.Second)
close(stopCh) //通知 worker退出
}
总结:
select{}
是特例,实际开发中应结合具体场景使用带操作的select-case-channel
。
- Go的并发模型核心在于通过channel和select优雅地协调goroutines。
内容由零声教学AI助手提供,问题来源于学员提问