MST

星途 面试题库

面试题:Go并发编程中如何优雅地处理多个超时任务

假设有多个Go协程执行不同的任务,每个任务都需要设置不同的超时时间,并且当任意一个任务超时时,其他任务也需要安全停止。请描述实现思路,并给出关键代码示例。
36.0万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
  1. 实现思路
    • 使用 context.Context 来管理协程的生命周期和超时。context.Context 可以在多个协程间传递,通过它可以控制协程的取消。
    • 为每个任务创建一个带有特定超时的 context.Context
    • 使用 sync.WaitGroup 来等待所有协程完成。
    • 监听一个全局的 context.Context,当任意一个任务超时时,取消这个全局 context.Context,进而取消其他所有任务。
  2. 关键代码示例
package main

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

func task(ctx context.Context, name string, timeout time.Duration, wg *sync.WaitGroup) {
    defer wg.Done()
    // 创建带有特定超时的context
    childCtx, cancel := context.WithTimeout(ctx, timeout)
    defer cancel()

    select {
    case <-childCtx.Done():
        if childCtx.Err() == context.DeadlineExceeded {
            fmt.Printf("%s任务超时\n", name)
        }
    case <-time.After(2 * timeout):
        // 模拟任务执行
        fmt.Printf("%s任务完成\n", name)
    }
}

func main() {
    var wg sync.WaitGroup
    // 创建全局context
    ctx, cancel := context.WithCancel(context.Background())

    tasks := []struct {
        name    string
        timeout time.Duration
    }{
        {"任务1", 2 * time.Second},
        {"任务2", 1 * time.Second},
        {"任务3", 3 * time.Second},
    }

    for _, taskInfo := range tasks {
        wg.Add(1)
        go task(ctx, taskInfo.name, taskInfo.timeout, &wg)
    }

    go func() {
        // 模拟检查任务超时
        time.Sleep(1 * time.Second)
        cancel()
        fmt.Println("有任务超时,取消所有任务")
    }()

    wg.Wait()
    fmt.Println("所有任务已处理完毕")
}

在上述代码中:

  • task 函数是每个任务的执行逻辑,它创建了自己带有超时的 context
  • main 函数中创建了全局的 context 用于控制所有任务,启动了多个任务协程,并使用 sync.WaitGroup 等待所有任务完成。
  • 另一个匿名协程模拟了检查任务超时并取消所有任务的逻辑。