golang_context
在 Go(Golang)中,context
是一个标准库提供的重要机制,用于控制协程(goroutine)之间的取消、超时、截止时间传递,以及上下文数据传递。它是并发编程中管理协程生命周期和避免资源泄漏的核心工具之一。
一、context
的主要用途
- 取消协程(Cancellation)
- 设置超时时间或截止时间(Timeout / Deadline)
- 跨 API 传递请求范围的数据(如认证信息、trace id)
- 防止资源泄露(确保任务完成或及时退出)
二、context
的基本接口和实现
接口定义(简化)
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key any) any
}
四个常用的 context
构造函数
函数 | 说明 |
---|---|
context.Background() | 最基础的 context,通常用于 main 函数、初始化或测试中 |
context.TODO() | 表示目前还不确定要用哪个 context(类似占位符) |
context.WithCancel(parent) | 返回一个新的 context 和一个取消函数,调用该函数会通知所有衍生的协程退出 |
context.WithTimeout(parent, timeout) | 指定超时自动取消 |
context.WithDeadline(parent, time.Time) | 指定某一时刻自动取消 |
context.WithValue(parent, key, val) | 传递 request-scoped 数据 |
三、使用示例
1. WithCancel
示例:手动取消任务
ctx, cancel := context.WithCancel(context.Background())
go func(ctx context.Context) {
select {
case <-ctx.Done():
fmt.Println("goroutine exit:", ctx.Err())
}
}(ctx)
time.Sleep(2 * time.Second)
cancel() // 手动取消
2. WithTimeout
示例:自动超时取消
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
select {
case <-time.After(3 * time.Second):
fmt.Println("operation done")
case <-ctx.Done():
fmt.Println("timeout:", ctx.Err()) // context deadline exceeded
}
3. WithValue
示例:传递请求数据
type key string
ctx := context.WithValue(context.Background(), key("userID"), 42)
func(ctx context.Context) {
val := ctx.Value(key("userID"))
fmt.Println("userID:", val)
}(ctx)
⚠️ 建议使用自定义类型作为 key,防止冲突。
四、协程泄露的场景与 context 的意义
很多时候,我们会在协程中执行耗时任务,如果外部请求被取消或超时,协程仍然运行,就会导致资源泄露。
通过 context
的 Done()
通道可以通知协程“取消”:
func doWork(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("work canceled")
return
default:
// 做一些事情
}
}
}
五、实践中常见的用法
- HTTP 请求生命周期管理(Go 标准库中的
http.Request
已经自带 context) - gRPC 调用(Context 是核心机制)
- 数据库查询、Redis、消息队列等外部服务调用
- 微服务链路追踪中传递 trace ID、user token 等
六、注意事项和最佳实践
- 尽量用 context 控制 goroutine 生命周期
- 不要滥用
WithValue
,它不是万能数据容器 - 及时调用
cancel()
,避免资源泄漏 - 避免在多个 goroutine 中共享一个可变 context(除非只读)
- 每层调用传入 context,形成清晰调用链
七、Go 标准库中 context 的使用案例
http.Request.WithContext(ctx)
:用于绑定 request 生命周期sql.DB.QueryContext(ctx)
:执行 SQL 时可以中途取消grpc.NewServer(...opts...)
:服务端方法自动接收 contextexec.CommandContext(ctx, ...)
:运行外部命令并能中断
总结
特性 | 描述 |
---|---|
可取消 | 协程间传递取消信号 |
超时控制 | 自动取消长时间运行的任务 |
数据传递 | 用于 request-scoped 数据 |
层级结构 | 子 context 会随着父 context 的取消而取消 |
通用接口 | 适用于标准库和第三方库中各种 IO、RPC、任务控制场景 |