本文深入探讨一段Go语言代码在使用无缓冲通道时的运行结果,并解释其背后的并发编程原理。代码利用Go语言通道特性,但在不同情况下展现出不同的行为,引发了关于通道阻塞、协程启动时间等问题的讨论。
代码如下:
package main import "fmt" func main() { chanint := make(chan int) defer close(chanInt) go func() { for { res, ok := <-chanInt if !ok { break //通道关闭则退出循环 } fmt.Println(res, ok) } }() chanInt <- 1 chanInt <- 10 }
这段代码创建了一个无缓冲通道chanInt,并启动一个goroutine从该通道接收数据并打印。主goroutine向通道发送1和10。然而,运行结果存在两种可能性:打印“1 true”和“10 true”,或仅打印“1 true”。这引发了以下疑问:
立即学习“go语言免费学习笔记(深入)”;
- 无缓冲通道输出结果存在两种情况的原因:
无缓冲通道的特性是:发送操作阻塞,直到有goroutine接收数据;接收操作也阻塞,直到有goroutine发送数据。主goroutine依次发送1和10。子goroutine接收并打印。如果子goroutine接收1的速度足够快,则在主goroutine发送10之前完成打印“1 true”,随后接收10并打印“10 true”。但如果子goroutine接收1的速度较慢,在主goroutine发送10并结束前,子goroutine可能只来得及接收并打印1。这是因为主goroutine结束导致通道关闭,子goroutine的接收操作会收到通道关闭的信号(ok为false),从而停止运行。
- 有缓冲通道(chanInt := make(chan int, 2))无输出的原因:
将通道改为容量为2的有缓冲通道后,主goroutine的发送操作不会阻塞,因为它有足够空间容纳两个数值。主goroutine发送完1和10后就结束,而子goroutine可能尚未启动或开始接收数据。这是因为协程的启动需要时间。如果主goroutine结束速度远快于子goroutine启动速度,子goroutine将无法从已关闭的通道中接收数据,导致没有任何输出。
通过分析,我们了解到Go语言并发编程中,协程启动时间、通道的阻塞/非阻塞特性以及主goroutine的结束时间共同决定最终输出结果。这些因素的相互作用导致结果的不确定性。 为了确保子goroutine能够处理所有发送的数据,应该在发送数据后添加等待机制,例如使用WaitGroup同步goroutine。