- Context传递基础
- 在Go语言中,
context.Context
是用于在goroutine之间传递截止时间、取消信号和其他请求范围值的主要方式。对于链式调用多个启动新goroutine的函数,要在每个函数调用时传递context.Context
。
- 例如,假设我们有函数
funcA
调用funcB
,funcB
调用funcC
:
func funcA(ctx context.Context) {
funcB(ctx)
}
func funcB(ctx context.Context) {
funcC(ctx)
}
func funcC(ctx context.Context) {
go func(ctx context.Context) {
// 执行具体业务逻辑
select {
case <-ctx.Done():
// 处理取消信号
return
default:
// 正常业务逻辑
}
}(ctx)
}
- 确保及时取消
- 在每个启动的goroutine中,使用
select
语句监听ctx.Done()
通道。当ctx.Done()
通道接收到数据时,意味着上层Context已取消,此时应尽快清理资源并退出goroutine。
- 例如,在一个模拟的数据库查询场景中:
func queryDatabase(ctx context.Context) {
db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/test")
if err!= nil {
// 处理错误
return
}
defer db.Close()
rows, err := db.QueryContext(ctx, "SELECT * FROM users")
if err!= nil {
// 处理查询错误
return
}
defer rows.Close()
for rows.Next() {
select {
case <-ctx.Done():
// 清理资源,关闭连接等
return
default:
// 处理查询结果
}
}
}
- 处理Context值覆盖或丢失问题
- 值覆盖:
- 确保在函数调用链中,不要意外地重新创建或覆盖
context.Context
。每个函数应使用接收到的ctx
作为参数传递给下一个函数,而不是创建新的ctx
。例如,不要在函数内部进行如下错误操作:
func wrongFunc(ctx context.Context) {
newCtx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
// 这里应该使用ctx传递给其他函数,而不是newCtx,否则会覆盖上层传递的取消逻辑
otherFunc(newCtx)
}
- 值丢失:
- 要确保在函数调用过程中不会丢失
ctx
。特别是在使用闭包时,要确保将ctx
正确地传递给闭包内部的goroutine。例如:
func correctFunc(ctx context.Context) {
go func(ctx context.Context) {
// 正确传递ctx
subFunc(ctx)
}(ctx)
}
- 还可以使用
context.Background()
作为顶层的Context起点,但在链式调用中,不要轻易在中间替换为context.Background()
,除非有明确的业务需求(这种情况很少见)。通常应保持从顶层传递下来的ctx
一致性,以确保取消信号能正确传播。