MST

星途 面试题库

面试题:Go中context在多个嵌套goroutine异步操作状态同步的应用

设想一个场景,在一个函数中启动多个goroutine,这些goroutine之间存在嵌套关系,例如一个主goroutine启动多个子goroutine,每个子goroutine又启动各自的孙goroutine。请详细说明如何使用context来确保在外部取消信号发出时,所有层次的goroutine都能正确地被取消,同时要处理好资源的清理工作,并给出实现思路和核心代码片段。
46.4万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
  1. 实现思路
    • 在主函数中创建一个 context.Contextcontext.CancelFunc,通过 context.WithCancel 方法。
    • context.Context 传递给主 goroutine
    • goroutine 在启动子 goroutine 时,将 context.Context 传递下去。同样,子 goroutine 在启动孙 goroutine 时也传递 context.Context
    • 每个 goroutine 内部通过监听 context.Context 的取消信号来决定是否退出。
    • 在取消信号发出后,每个 goroutine 要负责清理自己所占用的资源。
  2. 核心代码片段
package main

import (
    "context"
    "fmt"
    "sync"
    "time"
)

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    var wg sync.WaitGroup
    wg.Add(1)
    go mainGoroutine(ctx, &wg)
    // 模拟一段时间后发出取消信号
    time.Sleep(2 * time.Second)
    cancel()
    wg.Wait()
}

func mainGoroutine(ctx context.Context, wg *sync.WaitGroup) {
    defer wg.Done()
    var subWg sync.WaitGroup
    for i := 0; i < 3; i++ {
        subWg.Add(1)
        go subGoroutine(ctx, &subWg, i)
    }
    subWg.Wait()
}

func subGoroutine(ctx context.Context, wg *sync.WaitGroup) {
    defer wg.Done()
    var grandChildWg sync.WaitGroup
    for i := 0; i < 2; i++ {
        grandChildWg.Add(1)
        go grandChildGoroutine(ctx, &grandChildWg, i)
    }
    grandChildWg.Wait()
}

func grandChildGoroutine(ctx context.Context, wg *sync.WaitGroup, id int) {
    defer wg.Done()
    for {
        select {
        case <-ctx.Done():
            // 清理资源
            fmt.Printf("Grand - child goroutine %d cleaning up and exiting\n", id)
            return
        default:
            // 模拟工作
            fmt.Printf("Grand - child goroutine %d working\n", id)
            time.Sleep(200 * time.Millisecond)
        }
    }
}

在上述代码中:

  • main 函数创建了一个可取消的 context 并传递给 mainGoroutine
  • mainGoroutine 启动多个 subGoroutine 并传递 context
  • subGoroutine 启动多个 grandChildGoroutine 并传递 context
  • 每个 goroutine 内部通过 select 监听 ctx.Done() 信号,在接收到取消信号后进行资源清理并退出。