面试题答案
一键面试Go语言中Context的主要作用
Context主要用于在多个goroutine之间传递截止时间、取消信号和其他请求范围的值,以便在整个应用程序中进行协作式取消和控制。它可以帮助我们更好地管理goroutine的生命周期,避免资源泄漏,并在特定条件下优雅地停止goroutine。
常见应用场景及Context对goroutine的管理和控制
- 请求取消
- 场景:当一个HTTP请求被取消或超时,相关的所有goroutine也应该被取消,以避免不必要的计算和资源浪费。
- 管理和控制方式:在HTTP服务器处理请求时,将
context.Context
从顶层函数传递到所有启动的goroutine中。当请求取消时,context.Context
会发出取消信号,所有接收该context.Context
的goroutine可以通过检查ctx.Done()
通道是否关闭来决定是否停止执行。例如:
func handleRequest(ctx context.Context, w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
go func() {
select {
case <-ctx.Done():
return
// 业务逻辑
}
}()
}
- 设置截止时间
- 场景:某个操作需要在指定时间内完成,比如数据库查询操作,若超过设定时间还未完成则应取消操作。
- 管理和控制方式:通过
context.WithTimeout
或context.WithDeadline
创建带有截止时间的context.Context
。在goroutine中,使用select
语句监听ctx.Done()
通道和业务逻辑的通道。当截止时间到达,ctx.Done()
通道会关闭,goroutine可以据此停止执行。例如:
func databaseQuery(ctx context.Context) {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
resultCh := make(chan string)
go func() {
// 模拟数据库查询操作
time.Sleep(10 * time.Second)
resultCh <- "query result"
}()
select {
case result := <-resultCh:
fmt.Println(result)
case <-ctx.Done():
fmt.Println("query timeout")
}
}
- 传递请求范围的值
- 场景:在处理HTTP请求时,可能需要在多个中间件和业务逻辑函数组成的调用链中传递一些与请求相关的值,如用户认证信息等。
- 管理和控制方式:使用
context.WithValue
创建携带值的context.Context
,并将其传递给后续的函数和goroutine。在需要使用该值的地方,通过ctx.Value(key)
获取值。例如:
func middleware(ctx context.Context) context.Context {
ctx = context.WithValue(ctx, "userID", "12345")
return ctx
}
func handle(ctx context.Context) {
userID := ctx.Value("userID")
fmt.Println("User ID:", userID)
}