Go语言缓冲通道行为与预期不符,为何代码输出存在差异?
Go缓冲通道写入阻塞后仍输出16的原因分析
你的代码逻辑里,核心误解在于对goroutine阻塞和执行流程的判断,实际执行过程和预期有差异,具体拆解如下:
代码执行的完整流程
- 主goroutine启动,打印
main() started,创建容量为3的缓冲通道c,随后启动squaresgoroutine。 - 主goroutine依次向通道写入1、2、3,此时通道被填满,没有空闲位置。
- 主goroutine执行
c <- 4,因为通道已满,主goroutine进入阻塞状态,等待通道有接收者取走数据、腾出空间。 - 另一边,
squaresgoroutine的循环是固定执行4次读取操作:- 前三次读取通道里的1、2、3,分别输出1、4、9,此时通道变为空。
- 第四次循环时,
squares执行num := <-c,通道为空,所以squaresgoroutine也进入阻塞状态,等待通道有数据写入。
- 当
squares阻塞在第四次读取时,相当于给通道绑定了一个等待的接收者,此时主goroutine的c <-4阻塞被解除,4被写入通道的同时立即被squares读取,计算后输出16。 squares完成4次循环后退出,主goroutine恢复执行,打印main() stopped。
为什么和预期不符?
你预期squares处理完前3个值后会因通道空而阻塞,程序直接结束,但实际上squares的循环是硬编码的i < 4,它会坚持执行第四次读取操作。这次读取操作会触发主goroutine的写入操作完成,所以第四个值4会被处理,输出16。
如果想达到预期输出(不含16)
有两种调整方式:
- 把
squares的循环次数改成i < 3,只处理3个值; - 让
squares通过通道关闭来判断结束,主goroutine只写入1、2、3后关闭通道:
func squares(c chan int) { for num := range c { fmt.Println(num * num) } } func main() { fmt.Println("main() started") c := make(chan int, 3) go squares(c) c <- 1 c <- 2 c <- 3 close(c) fmt.Println("main() stopped") }
内容的提问来源于stack exchange,提问作者Jaydevsinh Chauhan
相关产品推荐
相关产品推荐

