Go基准测试中无缓冲通道代码阻塞差异原因咨询
无缓冲通道基准测试阻塞问题解析
核心原因
Go的基准测试框架逻辑很直接:当测试函数所在的goroutine执行完毕后,整个测试进程会被立即终止,所有未结束的goroutine都会被强制停止,不管这些goroutine是否还在运行或阻塞。
两段代码的差异分析
第一段(无法停止的代码)
func BenchmarkUnbufferedChannelInt(b *testing.B) { ch := make(chan int) go func() { for i := 0; i < b.N; i++ { ch <- i } }() for { <-ch } }
- 启动的goroutine完成
b.N次发送后就会退出,但通道不会自动关闭。 - 测试函数的主goroutine进入无限循环接收数据,当接收完
b.N次数据后,再执行<-ch会因为没有发送方而永久阻塞,导致测试函数的goroutine无法退出,测试进程也就一直卡在这儿。
第二段(能正常结束的代码)
func BenchmarkUnbufferedChannelInt(b *testing.B) { ch := make(chan int) go func() { for { <-ch } }() for i := 0; i < b.N; i++ { ch <- i } }
- 启动的goroutine是无限循环接收数据,但测试函数的主goroutine完成
b.N次发送后直接返回了。 - 测试框架检测到测试函数的goroutine已经结束,立刻终止整个测试进程,那个无限接收的goroutine被强制杀死,所以测试能正常输出结果并结束。
关于goroutine的补充说明
你说得没错,Go里goroutine之间没有父子关系,单个goroutine的终止不会影响其他goroutine,但这里的关键是测试进程的生命周期由测试函数的goroutine主导。只要测试函数返回,不管其他goroutine状态如何,进程都会被终止,这和main goroutine的特性类似——main goroutine退出时,整个进程也会终止,所有其他goroutine都会被强制停止。
如果想让第一段代码正常结束,可以在发送goroutine完成发送后关闭通道,然后主goroutine用for range ch接收数据,这样通道关闭后循环会自动退出:
func BenchmarkUnbufferedChannelInt(b *testing.B) { ch := make(chan int) go func() { for i := 0; i < b.N; i++ { ch <- i } close(ch) // 发送完成后关闭通道 }() // 接收直到通道关闭 for range ch { } }
内容的提问来源于stack exchange,提问作者didnlie23
相关产品推荐
相关产品推荐

