MST

星途 面试题库

面试题:Go中复杂场景下Goroutine生命周期协同管理

假设有一组相互依赖的goroutine,例如A依赖B的结果,B依赖C的结果,同时需要在外部统一控制这组goroutine的生命周期(如统一取消),如何设计一个健壮的方案来管理它们的生命周期,并且避免资源泄漏和死锁等问题,请详细阐述设计思路并给出关键代码片段。
30.4万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 使用context.Context来控制生命周期context.Context是Go语言中用于传递截止时间、取消信号和其他请求范围的值的机制。通过根context创建子context,可以将取消信号传递给所有相关的goroutine
  2. 确保依赖关系正确处理:每个goroutine在其执行逻辑中,需要在合适的时机检查context的取消信号,并且在完成任务后正确地清理资源。
  3. 使用sync.WaitGroup等待所有goroutine完成:用于同步goroutine,确保在外部控制取消后,所有goroutine都有机会进行清理工作并退出。

关键代码片段

package main

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

func main() {
    // 创建一个可取消的context
    ctx, cancel := context.WithCancel(context.Background())
    var wg sync.WaitGroup

    // C goroutine
    wg.Add(1)
    var resultC string
    go func(ctx context.Context) {
        defer wg.Done()
        select {
        case <-ctx.Done():
            return
        case <-time.After(2 * time.Second):
            resultC = "Result from C"
        }
    }(ctx)

    // B goroutine
    wg.Add(1)
    var resultB string
    go func(ctx context.Context) {
        defer wg.Done()
        // 等待C完成
        for {
            select {
            case <-ctx.Done():
                return
            case <-time.After(100 * time.Millisecond):
                if resultC != "" {
                    select {
                    case <-ctx.Done():
                        return
                    case <-time.After(1 * time.Second):
                        resultB = "Result from B based on " + resultC
                    }
                    return
                }
            }
        }
    }(ctx)

    // A goroutine
    wg.Add(1)
    go func(ctx context.Context) {
        defer wg.Done()
        // 等待B完成
        for {
            select {
            case <-ctx.Done():
                return
            case <-time.After(100 * time.Millisecond):
                if resultB != "" {
                    select {
                    case <-ctx.Done():
                        return
                    case <-time.After(1 * time.Second):
                        fmt.Println("Result from A based on ", resultB)
                    }
                    return
                }
            }
        }
    }(ctx)

    // 模拟一段时间后取消
    time.Sleep(5 * time.Second)
    cancel()
    wg.Wait()
    fmt.Println("All goroutines have finished.")
}

在上述代码中:

  1. 首先创建了一个可取消的context,并初始化一个WaitGroup
  2. CBA三个goroutine依次按照依赖关系执行任务,并在执行过程中不断检查context的取消信号。
  3. 外部通过cancel函数取消contextWaitGroup等待所有goroutine完成清理工作并退出。这样可以有效地管理goroutine的生命周期,避免资源泄漏和死锁。