面试题答案
一键面试1. 正确传递 context.Context
的方法
在Go语言中,通过将 context.Context
作为第一个参数在函数间传递来管理并发任务。以下面代码为例:
package main
import (
"context"
"fmt"
"time"
)
func inner(ctx context.Context) {
select {
case <-ctx.Done():
fmt.Println("inner: context cancelled or timed out")
return
case <-time.After(2 * time.Second):
fmt.Println("inner: normal completion")
}
}
func middle(ctx context.Context) {
inner(ctx)
}
func outer(ctx context.Context) {
middle(ctx)
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
outer(ctx)
}
在上述代码中,outer
函数调用 middle
,middle
又调用 inner
,每层函数都将 context.Context
作为第一个参数传递。当外层的 ctx
通过 context.WithTimeout
设置了超时,在 inner
函数中通过 select
监听 ctx.Done()
通道,一旦 ctx
被取消或超时,ctx.Done()
通道会被关闭,从而 inner
函数能及时响应。
2. 嵌套使用 context.Context
需注意的问题
资源泄漏
- 问题:如果在使用
context.Context
时没有正确处理取消操作,可能会导致资源泄漏。例如,在启动一个goroutine 后没有在ctx
取消时正确清理相关资源。 - 解决方法:在每个启动的goroutine 中,都要监听
ctx.Done()
通道,一旦通道关闭,及时清理资源。如:
func resourceIntensiveTask(ctx context.Context) {
go func() {
defer func() {
// 清理资源的逻辑
}()
select {
case <-ctx.Done():
return
case <-time.After(5 * time.Second):
// 任务逻辑
}
}()
}
上下文值的传递
- 问题:当使用
context.WithValue
给context.Context
设置值时,需要注意值的传递范围和生命周期。如果在不恰当的层级设置或获取值,可能导致数据不一致或错误。 - 解决方法:
- 设置值:只在必要的层级设置值,避免过度设置导致混乱。例如,只在需要共享数据的相关函数层级设置
context.WithValue
。 - 获取值:在需要使用值的地方获取,并且要进行类型断言确保类型正确。如:
- 设置值:只在必要的层级设置值,避免过度设置导致混乱。例如,只在需要共享数据的相关函数层级设置
ctxWithValue := context.WithValue(ctx, "key", "value")
value, ok := ctxWithValue.Value("key").(string)
if ok {
// 使用 value
}
同时,要避免在 context.Context
中传递敏感信息,因为 context.Context
可能会被记录日志等,导致敏感信息泄露。