MST
星途 面试题库

面试题:Go并发环境下Context的嵌套使用及注意事项

在一个复杂的Go并发程序中,存在多层函数调用,每层函数都需要传递 `context.Context` 来管理并发任务。例如,`func outer(ctx context.Context)` 调用 `func middle(ctx context.Context)`,`middle` 又调用 `func inner(ctx context.Context)`。请描述如何正确地在这些函数间传递 `context.Context`,以确保当外层的 `ctx` 被取消或超时,内层函数也能及时响应。并且说明在嵌套使用 `context.Context` 时有哪些需要特别注意的地方,例如资源泄漏、上下文值的传递等问题。
22.0万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

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 函数调用 middlemiddle 又调用 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.WithValuecontext.Context 设置值时,需要注意值的传递范围和生命周期。如果在不恰当的层级设置或获取值,可能导致数据不一致或错误。
  • 解决方法
    • 设置值:只在必要的层级设置值,避免过度设置导致混乱。例如,只在需要共享数据的相关函数层级设置 context.WithValue
    • 获取值:在需要使用值的地方获取,并且要进行类型断言确保类型正确。如:
ctxWithValue := context.WithValue(ctx, "key", "value")
value, ok := ctxWithValue.Value("key").(string)
if ok {
    // 使用 value
}

同时,要避免在 context.Context 中传递敏感信息,因为 context.Context 可能会被记录日志等,导致敏感信息泄露。