You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 15:07:17