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

该Goroutine是否会永久阻塞?缓冲通道是否为可行解决方案?

问题解答:Go协程阻塞与缓冲通道的作用

直接给你结论:当select选中context取消分支后,那个向无缓冲doneChan发送数据的goroutine会永久阻塞,进而导致goroutine泄漏;使用缓冲通道确实可以解决这个问题。

咱们一步步拆解分析:

1. 无缓冲通道为何会导致永久阻塞

先看你的原代码:

func test() error { 
    ctx, cancel := context.WithCancel(context.Background()) 
    cancel() 
    doneChan := make(chan bool) // 无缓冲通道
    go func() { 
        // 模拟长时间运行的任务
        time.Sleep(time.Minute) 
        doneChan <- true // 这里会永久阻塞吗?
    }() 
    select { 
    case <- ctx.Done(): 
        return ctx.Err() 
    case <- doneChan: 
        return nil 
    } 
}

当cancel()被调用后,ctx.Done()会立刻触发,select会优先选中这个分支,test函数随即返回。此时那个子goroutine还在执行time.Sleep,等它醒来执行doneChan <- true时,已经没有任何goroutine在等待接收这个通道的数据了——因为test函数已经退出,原本的select接收逻辑早已不存在。

而无缓冲通道的发送操作必须要有对应的接收操作才能完成,所以这个子goroutine会一直卡在doneChan <- true这一行,永远无法退出,造成goroutine泄漏。

2. 缓冲通道的解决方案

如果把doneChan改成带1个缓冲容量的通道,问题就迎刃而解了:

doneChan := make(chan bool, 1) // 缓冲通道,容量为1

当子goroutine执行doneChan <- true时,因为通道有一个缓冲空位,发送操作会直接完成,不需要等待接收方。这个goroutine发送完成后就会正常退出,不会出现阻塞和泄漏的情况。

简单来说,缓冲通道允许发送操作在没有即时接收方的情况下完成,只要通道的缓冲还有剩余空间。这里设置容量1刚好满足一次发送的需求,完美适配这个场景的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:12:05