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

为何移除merge函数中包裹wg.Wait()的goroutine会引发Go死锁?

Go管道merge函数移除wg.Wait()协程后死锁原因分析

问题描述

我阅读了Go官方博客的管道文章,尝试移除包裹wg.Wait()的goroutine后,出现了所有goroutine休眠的死锁问题,无法理解其中原因,原代码如下:

func merge(cs ...<-chan int) <-chan int {
    var wg sync.WaitGroup
    out := make(chan int)

    // Start an output goroutine for each input channel in cs.  output
    // copies values from c to out until c is closed, then calls wg.Done.
    output := func(c <-chan int) {
        for n := range c {
            out <- n
        }
        wg.Done()
    }
    wg.Add(len(cs))
    for _, c := range cs {
        go output(c)
    }

    // Start a goroutine to close out once all the output goroutines are
    // done.  This must start after the wg.Add call.
    go func() {
        wg.Wait()
        close(out)
    }()
    return out
}

死锁原因解析

当你移除包裹wg.Wait()的协程,直接在merge函数内调用wg.Wait()时,会形成一个死锁闭环:

  • merge函数阻塞:merge启动所有output协程后,调用wg.Wait()会让当前执行merge的goroutine停在这里,必须等所有output协程执行wg.Done()才能继续。
  • output协程阻塞:每个output协程在out <- n处卡住——因为out是无缓冲通道,发送操作必须等到有接收方才能完成。但此时merge还没返回out通道,调用merge的代码还没开始接收数据,所以output协程永远无法走到wg.Done()这一步。
  • 互相等待形成死锁:merge等output协程完成,output协程等out有接收方,接收方等merge返回out通道,所有goroutine都陷入休眠,触发死锁。

原代码把wg.Wait()放在单独协程里的核心作用,就是让merge函数能立刻返回out通道,让接收方可以开始接收数据,output协程的发送操作不会阻塞,完成后调用wg.Done(),最后那个独立协程等所有output完成后关闭out,整个流程就能正常推进。

内容的提问来源于stack exchange,提问作者Thai Nguyen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 04:45:30