在 Go 的并发编程中,goroutine 虽然轻量高效,但也容易“失控”——一旦启动就很难精确停止。为了控制并发协程的取消、超时、信号传递和生命周期管理,Go 官方引入了
context
包。
一、为什么需要 context?
假设一个请求需同时发起多个协程处理,如果客户端中断请求,你就需要及时关闭所有协程以节省资源。这就是 context
的典型应用场景:
- • ✅ 控制 goroutine 生命周期
- • ✅ 统一管理取消信号和超时
- • ✅ 传递跨 API 层的数据
二、context 的基本使用
Go 提供四种常见 context 创建方式:
context.Background() // 最初始的上下文,常用作根 context context.TODO() // 占位用,尚未确定如何传入 context 时使用 context.WithCancel(ctx) // 可调用 cancel() 主动取消 context.WithTimeout(ctx, d) // 指定超时时间 context.WithDeadline(ctx, t) // 指定截止时间
三、context.WithCancel 示例:主动取消
package main import ( "context" "fmt" "time" ) func worker(ctx context.Context, name string) { for { select { case <-ctx.Done(): fmt.Println(name, "被取消") return default: fmt.Println(name, "正在工作") time.Sleep(time.Second) } } } func main() { ctx, cancel := context.WithCancel(context.Background()) go worker(ctx, "worker1") go worker(ctx, "worker2") time.Sleep(3 * time.Second) cancel() // 通知所有携带该 context 的协程停止 time.Sleep(1 * time.Second) }
四、context.WithTimeout 示例:自动超时取消
func main() { ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() select { case <-time.After(5 * time.Second): fmt.Println("任务完成") case <-ctx.Done(): fmt.Println("超时取消:", ctx.Err()) } }
五、在 channel 模式中使用 context 控制 goroutine
结合 channel 和 worker pool:
func worker(ctx context.Context, jobs <-chan int, results chan<- int) { for { select { case <-ctx.Done(): return case job, ok := <-jobs: if !ok { return } results <- job * 2 } } }
六、context 的值传递功能
context
还可以在多个函数间传递一些只读值(例如:请求 ID、用户 token 等):
ctx := context.WithValue(context.Background(), "trace_id", "abc123") doSomething(ctx) func doSomething(ctx context.Context) { traceID := ctx.Value("trace_id") fmt.Println("Trace ID:", traceID) }
⚠️ 不推荐用它来传递业务参数,仅用于日志追踪、认证信息等不可变值。
七、最佳实践与注意事项
建议 | 描述 |
✅ 统一根 context | 每个服务入口处使用 context.Background() 或 context.TODO() |
✅ 及时调用 cancel() |
否则 goroutine 不会释放,造成泄露 |
✅ 在 goroutine 内监听 ctx.Done() |
接收到取消信号后及时退出 |
⚠️ 避免 context.Value 滥用 | 只传递与请求作用域相关的元信息 |
八、小结
- •
context
是 Go 并发模型中重要的资源控制机制; - • 能有效解决 goroutine 泄漏、超时处理、取消信号问题;
- • 与
goroutine + channel
搭配使用,可以构建健壮的并发系统。