必须调用cancel(),因其负责关闭done通道、停止内部timer并解除父子引用;不调用会导致timer驻留、goroutine泄漏及内存无法回收。

Context 不是并发的“启动开关”,而是所有已启动 goroutine 的“统一退场哨”。它本身不创建、不调度、不杀死 goroutine,只提供一个可监听的 Done() 通道和配套的取消/超时机制;goroutine 必须主动响应,否则就永远“听不见”。
哪些函数必须接收 ctx context.Context?
不是所有函数都要加 ctx 参数。硬塞进去反而让纯计算逻辑变重、难测试、易误传。
-
http.Client.Do、sql.DB.QueryContext、grpc.ClientConn.Invoke这类会阻塞等待外部响应的 I/O 调用,必须接收ctx - 启动长期 goroutine 的封装函数(如
startPolling(ctx)),且该 goroutine 内部需检查ctx.Done() - 业务函数若内部调用了上述 I/O 或启动了需受控 goroutine(如
fetchUserProfile(ctx, id)),也必须接收ctx -
calculateHash(data)、parseJSON(b)等纯内存操作,传ctx是冗余的,也不符合职责分离原则
为什么 defer cancel() 是铁律,而不是可选项?
漏掉 defer cancel() 是 goroutine 泄露最隐蔽的源头之一——timer 不释放、goroutine 持续存活,直到程序退出。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
-
context.WithTimeout和context.WithDeadline都依赖内部 timer goroutine,cancel()不仅关闭Done(),还会停止该 timer - 即使你只用了一次
ctx,只要派生了子 context,就必须调用对应cancel() - HTTP handler 中常见错误:用
r.Context()派生新 context 后忘记defer cancel(),导致每个请求都泄漏一个 timer - 嵌套派生时(如
ctx1 → ctx2 → ctx3),每个cancel都要单独 defer,不能只 defer 最外层的
goroutine 怎么才算真正“响应了 cancel”?
调用 cancel() 只是发通知,goroutine 是否退出,完全取决于它自己有没有“听”和“停”的逻辑。
- 必须显式监听
ctx.Done(),常用模式是select { case - 如果 goroutine 正在执行阻塞 I/O(如
conn.Read()),标准库不自动中断;需配合带 timeout 的net.Conn.SetReadDeadline()或使用支持 context 的封装(如http.Client) - 避免在 select 外单独读
,否则可能 panic(关闭 channel 后读取返回零值,但结构体零值对 <code>struct{}是合法的;不过若误用于其他类型会出问题) - 轮询类逻辑(如
for { doWork(); time.Sleep(...) })必须在循环开头加select { case ,否则可能错过信号
HTTP 请求里 context 到底控制哪一段?
很多人以为传了 ctx 就能“一键断连”,其实它只管发起阶段,不强制终止已建立连接上的数据流。
-
client.Do(req.WithContext(ctx))控制 DNS 解析、TCP 连接、TLS 握手、首字节到达前的等待 - 响应体正在读取时(尤其是大文件或流式响应),
ctx不会中断resp.Body.Read();需额外用io.LimitReader(resp.Body, max)或设置http.Transport.ResponseHeaderTimeout - Handler 内部务必用
r.Context(),而不是context.Background()—— 否则上游超时或客户端断连,你的 DB 查询、日志写入仍会继续执行 - 若下游服务支持,可在请求 header 中透传
ctx携带的 deadline 或 traceID,实现链路级协同控制
最常被忽略的是:context 的生命周期必须严格绑定到它的使用场景。一个 HTTP 请求的 context,绝不能被保存到全局变量、缓存或 goroutine 闭包中长期持有;一旦 handler 返回,这个 ctx 就该失效,再读 Done() 或取值都是未定义行为。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










