MST

星途 面试题库

面试题:Go Context在并发任务管理中的应用及原理

假设你有多个并发的Go协程任务,这些任务之间存在依赖关系,并且需要通过Context来控制整个任务流程的取消和超时。请详细阐述实现思路,并给出关键代码示例,同时说明Context在其中是如何传递和生效的。
39.5万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

实现思路

  1. 任务依赖建模:使用图(Graph)数据结构来表示任务之间的依赖关系。可以用邻接表等方式存储,每个节点代表一个任务,边表示任务间的依赖。
  2. 任务执行调度:利用Go语言的通道(Channel)来协调任务的执行顺序。只有当一个任务所依赖的所有任务都完成时,该任务才会被放入一个待执行队列,进而被Go协程执行。
  3. Context控制:通过context.Context来实现任务流程的取消和超时控制。context.Context可以传递到每个Go协程中,在协程内部通过ctx.Done()通道来监听取消信号。

关键代码示例

package main

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

// 定义任务结构体
type Task struct {
    ID       int
    DependsOn []int
    Execute  func(ctx context.Context) error
}

// 执行任务的函数
func executeTask(ctx context.Context, task Task, wg *sync.WaitGroup, resultChan chan<- error) {
    defer wg.Done()
    err := task.Execute(ctx)
    resultChan <- err
}

// 调度任务执行
func scheduleTasks(ctx context.Context, tasks []Task) error {
    var wg sync.WaitGroup
    resultChan := make(chan error, len(tasks))
    taskDone := make(map[int]bool)

    // 依赖关系检查和任务执行
    for {
        readyToExecute := false
        for _, task := range tasks {
            if taskDone[task.ID] {
                continue
            }
            allDependsDone := true
            for _, depID := range task.DependsOn {
                if!taskDone[depID] {
                    allDependsDone = false
                    break
                }
            }
            if allDependsDone {
                readyToExecute = true
                wg.Add(1)
                go executeTask(ctx, task, &wg, resultChan)
                taskDone[task.ID] = true
            }
        }
        if!readyToExecute {
            break
        }
    }

    go func() {
        wg.Wait()
        close(resultChan)
    }()

    // 检查任务执行结果
    for err := range resultChan {
        if err != nil {
            return err
        }
    }
    return nil
}

Context的传递和生效

  1. 传递context.Context通过函数参数传递到每个执行任务的Go协程中。例如在executeTask函数中,ctx作为参数传入。
  2. 生效:在每个任务的执行函数task.Execute内部,可以通过ctx.Done()通道监听取消信号。如果ctx.Done()通道接收到数据,说明Context被取消或超时,任务应尽快结束执行。例如:
func myTaskExecute(ctx context.Context) error {
    select {
    case <-ctx.Done():
        return ctx.Err()
    default:
        // 任务正常执行逻辑
        fmt.Println("Task is running")
        return nil
    }
}

在上述代码中,当ctx.Done()通道接收到信号时,任务通过返回ctx.Err()来结束执行,从而实现整个任务流程的取消或超时控制。