Go context包专为控制生命周期和传递取消信号设计,不可用于传业务参数;必须用WithCancel的场景是需主动终止长期goroutine;WithTimeout基于相对时长,WithDeadline基于绝对时间点;WithValue仅适用于请求级只读元数据,业务参数应显式传递。
Go 的 context 包不是用来“传递参数”的,而是专为控制生命周期、传播取消信号和传递请求范围的只读值而设计;滥用它传业务数据或频繁修改会导致代码难维护、内存泄漏或 goroutine 泄漏。
什么时候必须用 context.WithCancel?
当你启动一个可能长期运行、且需要被外部主动终止的 goroutine 时,context.WithCancel 是最直接的手段。典型场景包括:HTTP 请求处理中启后台轮询、数据库连接池超时等待、长连接心跳协程。
- 调用
context.WithCancel返回的cancel函数必须在确定不再需要子 goroutine 时显式调用(比如 handler 返回前、defer 中) - 不调用
cancel→ 子 goroutine 持有父 context 引用 → 父 context 无法被 GC → 可能泄露内存和 goroutine - 同一个 context 被多次
cancel()是安全的,但无实际意义;重复 defercancel可能掩盖逻辑错误
示例:
ctx, cancel := context.WithCancel(r.Context()) defer cancel() // 必须放 handler 顶层 defer,不能只在某分支里go func() { for { select { case <-ctx.Done(): return // 收到取消信号,退出 default: // 执行工作 time.Sleep(1 * time.Second) } } }()
context.WithTimeout 和 context.WithDeadline 的区别在哪?
二者都用于自动触发取消,但时间语义不同:WithTimeout 是相对当前时间的持续时长,WithDeadline 是绝对时间点。选错会导致超时行为不符合预期。