done channel 是最主流的 goroutine 退出通知方式,需由子 goroutine 主动在 select 中监听 done 通道并自行终止,而非主 goroutine 单方面关闭后等待。

如何用 done channel 正确通知 goroutine 退出
Go 中没有“强制杀掉 goroutine”的机制,done channel 是最主流、最可控的退出通知方式。它不是用来“发送退出信号”,而是让被调用方主动监听并决定何时收尾。
常见错误是只在主 goroutine 关闭 done,但子 goroutine 没有 select + case 分支,或漏了 <code>default 导致阻塞卡死。
- 必须用
select配合donechannel,不能直接同步等待 -
done通常声明为(只读),避免子 goroutine 误关它 - 如果 goroutine 内部有长耗时操作(如 HTTP 调用、文件读写),需配合
context.Context的超时/取消,仅靠done无法中断阻塞系统调用
context.WithCancel 和纯 done channel 的区别在哪
纯 done channel 只解决“通知退出”,不提供“传播取消”或“携带值”的能力;而 context.WithCancel 返回的 ctx.Done() 本质也是个 ,但它能嵌套、能超时、能传 <code>Value,且和标准库(如 http.Client、net.Listener)天然兼容。
- 自己写的简单循环任务,用
make(chan struct{})+close()完全够用 - 涉及 HTTP 请求、数据库查询、子 goroutine 嵌套调用时,必须用
context.Context,否则无法传递取消信号到下层 - 不要把
ctx.Done()和自定义donechannel 混着监听却没统一关闭逻辑——容易漏关、重复关或 panic
goroutine 退出后资源没释放?检查这三处
goroutine 退出不代表关联资源自动清理,尤其当它持有文件句柄、网络连接、计时器或未缓冲 channel 发送端时,很容易泄漏。
立即学习“go语言免费学习笔记(深入)”;
- 用
defer关闭文件、连接、取消time.Timer,别依赖 goroutine 退出时自动回收 - 向无缓冲 channel 发送数据前,确保有接收方;否则 goroutine 会永久阻塞在
ch ,根本走不到 defer - 启动 goroutine 时若传入了 closure 捕获了大对象(如整个 struct),记得确认是否真需要,避免内存长期驻留
为什么 select 里加 default 有时反而导致忙等
加 default 是为了防止阻塞,但若逻辑本该等待退出信号,却写成“立刻返回再重试”,就变成空转消耗 CPU。
- 适合场景:非关键轮询(比如每秒查一次状态),且退出不敏感
- 不适合场景:等待外部事件(如消息到达、定时触发),此时应去掉
default,老老实实select等done或业务 channel - 折中方案:用
time.After或time.Tick控制间隔,而不是裸default
真正难的不是写一个 done channel,而是判断哪些 goroutine 必须响应退出、哪些可以自然结束,以及退出路径上有没有隐式依赖——比如 A goroutine 关了 done,B 却还在往 C channel 发数据,而 C 已经退出,那 B 就卡死了。










