基于Go协程与通道的斐波那契代码运行原理问询
Go协程+通道实现斐波那契序列的疑问解答
先贴出Rosetta Code上常见的对应实现代码:
package main import "fmt" func fib(c, quit chan int) { x, y := 0, 1 for { select { case c <- x: x, y = y, x+y case <-quit: fmt.Println("fib协程退出") return } } } func main() { c := make(chan int) quit := make(chan int) // 启动匿名协程负责接收数值并触发停止 go func() { for i := 0; i < 10; i++ { fmt.Println(<-c) } quit <- 0 // 发送停止信号 }() fib(c, quit) }
1. fib函数里的无限for循环怎么停止?
fib里的for确实是无限循环,但它被select语句包裹着。当quit通道收到值时,case <-quit:分支会被触发,执行return直接退出fib函数,循环自然就终止了。
2. 通道c是如何传递停止信号的?
先澄清:在上面的代码里,传递停止信号的是quit通道,c的作用是传递斐波那契数值。如果你的代码里是用c来传停止信号,大概率是另一种单通道实现:
func fib(c chan int) { a, b := 0, 1 for { select { case c <- a: a, b = b, a+b case <-c: return } } } func main() { c := make(chan int) go func() { for i := 0; i < 10; i++ { fmt.Println(<-c) } c <- 0 // 往c发停止信号 }() fib(c) }
这种情况下,c兼具两个作用:fib协程往里面塞斐波那契值,接收协程取完指定数量后,往c里塞一个值,fib里的case <-c:分支触发,执行return退出循环。
3. 函数调用的逻辑顺序是怎样的?
以双通道版本为例:
- 主协程先创建
c(传数值)和quit(传停止信号)两个通道。 - 启动匿名协程:这个协程会循环10次,每次从c里取斐波那契值打印,取完10次后往quit通道发一个值。
- 主协程调用
fib(c, quit),进入fib的无限循环。 - fib里的
select会优先尝试往c发值(此时匿名协程在等c的值,通道不阻塞),每次发完更新斐波那契数值。 - 匿名协程取完10个值后,往quit发0,fib的select检测到quit有值,触发退出分支,fib函数返回。
- 主协程执行完毕,程序退出。
内容的提问来源于stack exchange,提问作者Rudolf Fanchini
相关产品推荐
相关产品推荐

