go context包用于并发控制,提供取消信号、超时控制和请求范围数据传递;需显式传参、配对调用cancel、避免滥用withvalue,并在i/o中通过select监听done()。

Go 的 context 包是并发控制的核心工具,它不负责启动或管理 goroutine,而是为它们提供统一的取消信号、超时控制和跨调用链传递请求范围数据的能力。关键在于:所有可能被取消或需要设限的操作(如 HTTP 请求、数据库查询、子任务等待)都应接受 context.Context 参数,并在 Done() 通道关闭时及时退出。
取消传播:父子 Context 的链式终止
通过 context.WithCancel(parent) 创建子 context,父 context 被取消时,所有直接/间接派生的子 context 会同步关闭其 Done() 通道。这天然支持“树状”取消传播——比如一个 HTTP handler 启动多个 goroutine 处理子任务,只需 cancel 父 context,所有子 goroutine 都能感知并退出。
- 不要手动关闭子 context 的
Done()通道;必须调用返回的cancel()函数 - 每个
WithCancel必须配对调用cancel(),否则可能泄露内存和 goroutine - 避免将 context 存入结构体长期持有;应作为参数显式传入每个可能阻塞的函数
超时控制:WithTimeout 与 WithDeadline 的区别
WithTimeout(parent, d) 是基于当前时间 + 持续时间的相对超时;WithDeadline(parent, t) 是绝对截止时间。两者都会自动触发取消,但语义不同:前者适合“最多等 5 秒”,后者适合“必须在下午 3 点前完成”。底层都使用定时器,超时后自动调用内部 cancel()。
- 超时 context 也需手动调用
cancel()(即使已超时),以释放关联的 timer 和 goroutine - 若操作本身支持 deadline(如
net.Conn.SetDeadline),应优先使用原生机制,再辅以 context 做兜底 - 避免嵌套多层超时 context;一个请求链通常只在入口处设一次 timeout,下游复用即可
值传递:WithValue 的安全使用边界
context.WithValue(parent, key, val) 仅适用于传递请求范围的、不可变的元数据(如用户 ID、请求 trace ID、认证 token)。它不是通用的状态容器,更不该用来传配置、函数或可变对象。
立即学习“go语言免费学习笔记(深入)”;
- key 必须是可比较类型,推荐自定义未导出的 struct 或指针,避免字符串 key 冲突
- 值应是只读的;若需修改,请用外部同步机制(如 mutex),而非依赖 context 更新
- 不要用
WithValue替代函数参数——清晰的接口比隐式上下文更易测试和维护
实际协作:I/O 操作中配合 Done() 与 select
任何可能阻塞的调用(如 time.Sleep、ch 、<code>、<code>http.Do)都应参与 select,监听 ctx.Done()。一旦触发,立即清理资源并返回错误(通常是 ctx.Err())。
- 示例:
select { case - 对 channel 操作,记得在退出前关闭或清空,防止 goroutine 泄露
- 第三方库(如 database/sql、net/http)大多已内置 context 支持,优先使用带 context 的方法(如
db.QueryContext)










