1. 使用context实现任务取消
- 创建带取消功能的context:在Go中,通过
context.WithCancel
函数可以创建一个可取消的context.Context
。例如:
parentCtx := context.Background()
ctx, cancel := context.WithCancel(parentCtx)
- 在goroutine中使用context:在启动的每个并发任务(goroutine)中传入
ctx
,并在任务执行过程中定期检查ctx.Done()
通道是否关闭。当cancel()
函数被调用时,ctx.Done()
通道会被关闭,任务可以据此进行清理工作并退出。
go func(ctx context.Context) {
for {
select {
case <-ctx.Done():
// 清理资源
return
default:
// 执行任务逻辑
}
}
}(ctx)
- 取消任务:在需要取消任务的地方调用
cancel()
函数,所有监听该ctx
的goroutine会收到取消信号。
2. 使用context实现超时控制
- 创建带超时的context:使用
context.WithTimeout
函数创建一个带超时的context.Context
。它接受一个父context
、超时时间作为参数。
parentCtx := context.Background()
ctx, cancel := context.WithTimeout(parentCtx, 5*time.Second)
defer cancel()
- 任务执行与超时处理:在goroutine中同样通过监听
ctx.Done()
通道来处理超时情况。当超时时间到达,ctx.Done()
通道会被关闭,任务进行清理并退出。
go func(ctx context.Context) {
select {
case <-ctx.Done():
// 处理超时,清理资源
return
default:
// 执行任务逻辑
}
}(ctx)
3. 使用context实现数据传递
- 创建带值的context:通过
context.WithValue
函数创建一个携带值的context.Context
。它接受一个父context
、键值对作为参数。键应该是具有唯一性的类型,通常使用结构体指针。
type key struct{}
parentCtx := context.Background()
ctx := context.WithValue(parentCtx, key{}, "data value")
- 获取传递的数据:在需要获取数据的地方,通过
ctx.Value(key)
来获取对应的值。注意需要进行类型断言。
data, ok := ctx.Value(key{}).(string)
if ok {
// 使用数据
}
4. 高并发场景下优化context的使用
- 复用context:尽量复用已有的
context
,避免频繁创建新的context
。例如,在一个函数调用链中,传递同一个context
,而不是在每个函数内部重新创建。
- 及时取消context:在任务完成或者不再需要时,及时调用取消函数(如
cancel()
),避免context
一直存活导致资源浪费。
- 避免不必要的context传递:如果某个goroutine不需要处理取消、超时等功能,就不需要传递
context
,减少不必要的开销。
- 控制context树的深度:复杂的
context
嵌套(即context
树很深)可能导致性能问题。尽量保持context
树的扁平化,减少嵌套层次。