在 Go 的并发编程中,goroutine 虽然轻量高效,但也容易"失控"------一旦启动就很难精确停止。为了控制并发协程的取消、超时、信号传递和生命周期管理 ,Go 官方引入了
context
包。
一、为什么需要 context?
假设一个请求需同时发起多个协程处理,如果客户端中断请求,你就需要及时关闭所有协程以节省资源。这就是 context
的典型应用场景:
- • ✅ 控制 goroutine 生命周期
- • ✅ 统一管理取消信号和超时
- • ✅ 传递跨 API 层的数据
二、context 的基本使用
Go 提供四种常见 context 创建方式:
scss
context.Background() // 最初始的上下文,常用作根 context
context.TODO() // 占位用,尚未确定如何传入 context 时使用
context.WithCancel(ctx) // 可调用 cancel() 主动取消
context.WithTimeout(ctx, d) // 指定超时时间
context.WithDeadline(ctx, t) // 指定截止时间
三、context.WithCancel 示例:主动取消
go
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 示例:自动超时取消
css
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:
go
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 等):
css
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
搭配使用,可以构建健壮的并发系统。