MST

星途 面试题库

面试题:Go定时任务中context的复杂场景应用

在一个包含多个子定时任务的复杂Go程序中,主任务需要等待所有子定时任务完成后再统一处理结果,但又要能在特定条件下快速取消所有子任务。请详细阐述如何合理使用context来实现这种需求,包括context的传递、取消逻辑以及资源清理等方面,同时给出核心代码片段。
45.5万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
  1. Context的传递
    • 在Go中,context.Context通常作为函数参数在不同的函数之间传递,特别是在启动子定时任务的函数中。
    • 可以使用context.Background()作为根上下文,然后基于这个根上下文衍生出带取消功能的上下文。
  2. 取消逻辑
    • 使用context.WithCancel函数创建一个可取消的上下文。该函数返回一个新的上下文和一个取消函数。当需要取消所有子定时任务时,调用这个取消函数。
    • 在每个子定时任务的循环中,定期检查上下文的Done通道。如果Done通道接收到数据,说明上下文已被取消,子任务应立即停止。
  3. 资源清理
    • 子任务在接收到取消信号后,应负责清理自己占用的资源,比如关闭文件描述符、数据库连接等。
    • 可以使用Go语言的defer语句来确保资源在函数结束时被正确清理。

以下是核心代码片段:

package main

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

func subTask(ctx context.Context, taskID int, wg *sync.WaitGroup) {
    defer wg.Done()
    for {
        select {
        case <-ctx.Done():
            // 清理资源
            fmt.Printf("Task %d cancelled\n", taskID)
            return
        default:
            // 模拟子任务工作
            fmt.Printf("Task %d working\n", taskID)
            time.Sleep(time.Second)
        }
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    var wg sync.WaitGroup

    numTasks := 3
    for i := 0; i < numTasks; i++ {
        wg.Add(1)
        go subTask(ctx, i, &wg)
    }

    // 模拟特定条件触发取消
    time.Sleep(3 * time.Second)
    cancel()

    // 等待所有子任务完成
    wg.Wait()
    fmt.Println("All tasks completed or cancelled")
}

在上述代码中:

  • context.WithCancel创建了一个可取消的上下文ctx和取消函数cancel
  • subTask函数在循环中通过select语句监听ctx.Done()通道,一旦接收到取消信号,就进行资源清理并返回。
  • main函数中,启动了多个子任务,并在特定条件下(这里是3秒后)调用cancel函数取消所有子任务,最后通过wg.Wait()等待所有子任务完成。