
本文深入剖析 Go Tour 中经典的斐波那契并发示例,清晰解释 select 的阻塞选择机制、无缓冲通道的同步特性,以及主 goroutine 与工作 goroutine 如何通过通道精确协同完成 10 项计算并优雅退出。
本文深入剖析 go tour 中经典的斐波那契并发示例,清晰解释 `select` 的阻塞选择机制、无缓冲通道的同步特性,以及主 goroutine 与工作 goroutine 如何通过通道精确协同完成 10 项计算并优雅退出。
这个示例是理解 Go 并发模型核心思想——通信优于共享内存——的绝佳入口。它看似简短,却浓缩了通道(channel)、goroutine、select 多路复用三大关键概念的精妙配合。
核心流程:双 goroutine 协同工作
程序启动时,main 函数创建两个无缓冲通道:c(用于传输斐波那契数)和 quit(用于发送退出信号)。随后立即启动一个匿名 goroutine:
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c) // ① 阻塞等待接收
}
quit <- 0 // ② 发送退出信号
}()与此同时,main 线程继续执行 fibonacci(c, quit)。该函数进入一个无限 select 循环:
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x: // ③ 尝试向 c 发送当前值
x, y = y, x + y
case <-quit: // ④ 尝试从 quit 接收信号
fmt.Println("quit")
return
}
}
}关键机制:无缓冲通道的“握手式”同步
由于 c 是无缓冲通道,发送(c )和接收(<code>)必须同时就绪才能完成通信。这导致以下严格同步行为:
-
首次循环:goroutine 在
fmt.Println( 处阻塞(等待数据);<code>fibonacci在case c 处阻塞(等待接收方)。二者相互等待,形成“握手”。 -
握手完成:当两者同时准备好,
0被成功发送并打印,x, y更新为1, 1。 -
后续迭代:重复上述握手过程,依次输出
0, 1, 1, 2, 3, 5, 8, 13, 21, 34(共 10 个数)。
退出机制:quit 通道打破循环
当 goroutine 完成 10 次接收后,执行 quit 。此时 <code>fibonacci 的 select 立即检测到 quit 通道有可接收数据,优先执行 case 分支(因为 c 仍处于阻塞状态:goroutine 已退出,无人再接收),打印 <code>"quit" 并 return,整个函数终止。
⚠️ 注意事项:
- 若移除
quit逻辑,fibonacci将永远阻塞在c (因 goroutine 结束后 <code>c无人接收),程序无法退出。- 尝试在
main结尾添加会引发 panic:<code>fatal error: all goroutines are asleep - deadlock,印证了无缓冲通道必须配对收发。select不是轮询,而是阻塞等待任意一个 case 就绪;多个 case 同时就绪时,Go 会伪随机选择(避免饥饿),但本例中因同步设计,顺序严格确定。
这种基于通道的同步模型,消除了显式锁和条件变量的复杂性,使并发逻辑更直观、更安全——这正是 Go 并发哲学的精髓所在。

















