MST

星途 面试题库

面试题:Go Context在复杂并发任务树中的传递与管理

在一个具有多层嵌套的并发任务结构中,上层任务需要根据某些条件取消所有下层任务。例如,有一个主任务,它会启动多个子任务,每个子任务又会启动各自的孙任务。如何通过Context机制来实现当主任务取消时,所有子任务和孙任务都能正确且及时地被取消?请详细阐述Context的传递方式和取消逻辑,并给出核心代码示例。
11.4万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
  1. Context的传递方式
    • 在Go语言中,context.Context通常通过函数参数在不同的任务(函数)之间传递。对于主任务、子任务和孙任务,主任务创建一个context.Context,一般是context.WithCancel创建的可取消的上下文。然后将这个上下文作为参数传递给每个子任务。子任务再将接收到的上下文传递给各自的孙任务。
  2. 取消逻辑
    • 主任务通过调用cancel函数(context.WithCancel返回的取消函数)来发起取消操作。这个取消信号会沿着上下文传递链,传递到子任务和孙任务。子任务和孙任务在执行过程中,需要定期检查上下文的取消状态。当检测到取消信号时,应尽快停止正在执行的操作,并返回。
  3. 核心代码示例(以Go语言为例)
package main

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

// 孙任务函数
func grandChildTask(ctx context.Context, taskID int) {
    for {
        select {
        case <-ctx.Done():
            fmt.Printf("Grand - Child Task %d cancelled\n", taskID)
            return
        default:
            fmt.Printf("Grand - Child Task %d is running\n", taskID)
            time.Sleep(200 * time.Millisecond)
        }
    }
}

// 子任务函数
func childTask(ctx context.Context, taskID int) {
    // 创建子任务自己的上下文,这样即使子任务提前结束,孙任务也能正确取消
    childCtx, childCancel := context.WithCancel(ctx)
    defer childCancel()

    // 启动多个孙任务
    for i := 0; i < 2; i++ {
        go grandChildTask(childCtx, taskID*10 + i)
    }

    for {
        select {
        case <-ctx.Done():
            fmt.Printf("Child Task %d cancelled\n", taskID)
            return
        default:
            fmt.Printf("Child Task %d is running\n", taskID)
            time.Sleep(500 * time.Millisecond)
        }
    }
}

func main() {
    // 创建主任务的上下文
    ctx, cancel := context.WithCancel(context.Background())

    // 启动多个子任务
    for i := 0; i < 2; i++ {
        go childTask(ctx, i)
    }

    // 模拟主任务运行一段时间后取消
    time.Sleep(2 * time.Second)
    cancel()

    // 等待一段时间,确保所有任务都有机会处理取消信号
    time.Sleep(2 * time.Second)
}

在上述代码中:

  • main函数创建了一个可取消的上下文ctx,并启动了多个子任务。
  • 每个子任务childTask创建了自己的可取消上下文childCtx,并启动多个孙任务grandChildTask
  • 子任务和孙任务都通过select语句监听上下文的取消信号,当收到取消信号时,打印取消信息并返回,从而实现了主任务取消时,所有子任务和孙任务都能正确且及时地被取消。