context.WithCancel 返回新 context 和 cancel 函数,调用 cancel 即标记完成并关闭 Done channel;WithTimeout 基于相对时间,WithDeadline 基于绝对时间且更抗系统时钟调整。

context 取消信号不是靠“传”出来的,而是靠 context.WithCancel 创建的父子关系联动生效的——你改了子 context 的状态,父 context 不会感知;但父 cancel 了,所有子自动失效。
为什么 context.WithCancel 返回两个值?
它返回一个新 context.Context 和一个 cancel 函数。这个 cancel 不是“发信号”,而是“触发关闭”:调用它,就立刻把该 context 标记为 Done,并关闭其 Done() channel。
- 不调用
cancel(),子 context 就永远不会结束,哪怕父 context 已 cancel(除非是WithTimeout这类带自动终止的) - 多次调用同一个
cancel()是安全的,但第二次起什么也不做 - 如果忘了 defer
cancel(),会导致 goroutine 泄漏或资源长期占用(比如数据库连接、HTTP 连接)
context.WithTimeout 和 context.WithDeadline 的区别在哪?
两者都自动 cancel,但触发条件不同:WithTimeout 是相对当前时间加偏移,WithDeadline 是绝对时间点。选错会导致超时逻辑错乱。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
-
context.WithTimeout(ctx, 5*time.Second):从调用那一刻起,5 秒后自动 cancel -
context.WithDeadline(ctx, time.Now().Add(5*time.Second)):效果看似一样,但若系统时间被 NTP 调整过,WithDeadline更可靠(它依赖 monotonic clock) - 在 HTTP server 中,通常用
WithTimeout;在定时任务或与外部服务约定截止时间时,WithDeadline更准
goroutine 里怎么正确监听取消?
不能只检查 ctx.Done() 是否关闭,还得处理 ctx.Err() 来区分是超时、取消,还是别的原因。
立即学习“go语言免费学习笔记(深入)”;
- 永远用
select { case ,别用 <code>if ctx.Err() != nil—— 因为Done()channel 关闭后,Err()才有意义 - 在循环中监听时,每次迭代都要 select,否则可能卡死(比如网络读写阻塞时)
- 调用阻塞函数(如
http.Client.Do、time.Sleep)前,确保它们本身支持 context(如用http.NewRequestWithContext),否则 cancel 无效
真正难的不是调用 WithCancel,而是决定在哪里 call cancel()、什么时候该把它传进 goroutine、以及哪些地方必须主动检查 ctx.Err() 并清理资源——这些地方一漏,context 就只是个摆设。

















