context取消仅广播通知,不自动终止goroutine;必须在循环或阻塞点主动监听ctx.done(),仅检查一次ctx.err()或漏写select的case会导致取消失效。

Context取消信号不会自动终止goroutine,它只发通知;真正退出必须由你写代码响应Done通道。
ctx.Done() 必须在循环或阻塞点主动监听
很多人以为调用 cancel() 就能让子goroutine立刻停,其实不是。Go的Context取消是“广播通知”,不是“强制杀掉”。如果goroutine里没监听 ctx.Done(),它根本收不到信号。
- 只检查一次
ctx.Err() != nil→ 后续取消完全无效 - 用了
select但漏了case → 卡死在其它分支(比如 <code>case data := ,而 <code>ch已关但没default) - HTTP handler 中启新 goroutine 却传了
context.Background()→ 完全脱离请求生命周期
正确做法是在每个可能阻塞的位置(尤其是 for 循环、channel 操作、sleep 前)都放一个 select 监听 ctx.Done()。
WithCancel 嵌套多层时取消信号仍穿透,但 cancel() 调用时机很关键
父子 Context 的取消链是可靠的:只要子 context 是通过 context.WithCancel(parent) 正确派生的,父级 cancel() 就会触发所有后代的 Done() 关闭。这不是靠轮询,而是底层 cancelCtx 结构体维护的双向引用。
- 中间某层自己调过
cancel()→ 它的Done()已关,之后从它派生的子 context 就不再响应更上层的取消(channel 无法二次关闭) - 把
cancel函数传进 goroutine 是高危操作 → 容易误调、重复调、或 goroutine 提前 panic 导致没调 - HTTP handler 中推荐在写完响应后立即调
cancel(),而不是defer cancel()→ 因为可能提前return,defer就没机会执行
第三方库不支持 context 时怎么处理取消
不是所有库都暴露 context.Context 参数,这时候硬塞没用,得换思路。
- 先查文档:有些库(如
http.DefaultClient)内部其实用了标准库的带 context 版本,只需替换为&http.Client{Timeout: ...}或显式用client.Do(req.WithContext(ctx)) - 纯阻塞调用(如 Cgo、syscall、无 context 接口的 SDK)→ 用 wrapper goroutine 包一层,外部通过
ctx.Done()控制中断逻辑,比如关掉依赖 channel、设atomic.Bool标志位、或调它的Stop()方法 - 别试图用
runtime.Goexit()或信号强杀 → Go 不支持 goroutine 级别强制终止,这么做大概率导致资源泄漏或 panic
最常被忽略的一点:取消不是终点,而是清理的起点。收到 ctx.Done() 后,你得自己关文件、断连接、释放锁、停止 ticker —— Context 只负责通知,不替你干活。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











