本文深入解析 Go select 语句中对通道的读写行为,重点说明重复接收(
本文深入解析 go `select` 语句中对通道的读写行为,重点说明重复接收(`
Go 的 select 语句是实现非阻塞或多路通道通信的核心机制,其行为类似于 switch,但每个 case 都对应一个通道操作(发送或接收)。关键在于:每个 ——它不是“窥探”或“检查”,而是原子性的“接收并移除”。
在原始正确代码中:
case s := <-quit:
fmt.Println("quit =", s)
return
而修改后的错误代码:
case <-quit:
fmt.Println(<-quit) // ⚠️ 危险!第二次接收问题出现在第二行:case 再次接收,但 main goroutine 中只向 quit 发送了一次(quit 无人再向 quit 发送新值。因此,
✅ 正确实践原则
- 一个 :避免在同一个逻辑路径中多次对同一通道执行接收操作。
- 接收后立即使用或丢弃:若只需信号语义(如退出通知),可直接用空接收 case
- 确保发送方与接收方配对:本例中 quit 仅发送一次,接收逻辑也必须严格匹配一次。
? 改进示例(增强健壮性)
为更清晰体现信号语义,可进一步优化退出逻辑:
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit: // 纯信号,无需读取具体值
fmt.Println("received quit signal")
return
}
}
}
func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 9 // 发送退出信号
}()
fibonacci(c, quit)
}? 小结:select 的每个 case 是独立的通道操作,










