MST

星途 面试题库

面试题:Go中Context超时控制在并发场景下的应用

假设有多个并发的Go协程,每个协程执行不同的任务,任务之间可能存在依赖关系。请阐述如何使用Context来统一管理这些并发协程的超时控制,以确保在主程序设置的整体超时时间内,如果某个协程超时,其他相关协程也能及时停止,并且要说明如何处理协程之间的依赖关系。同时写出相关示例代码来展示实现过程。
41.7万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

1. 使用Context管理并发协程超时控制

在Go语言中,context.Context 主要用于在不同的Go协程之间传递截止时间、取消信号等。以下是如何使用 Context 统一管理并发协程的超时控制,以及处理协程之间依赖关系的步骤:

  1. 创建带超时的Context:在主程序中,使用 context.WithTimeout 创建一个带超时时间的 Context。这个 Context 会在超时时间到达时自动取消。
  2. 传递Context:将创建的 Context 传递给所有需要管理超时的协程。每个协程在执行任务时,需要检查 Context 的取消信号,以便在超时或被取消时及时停止。
  3. 处理协程依赖关系:对于存在依赖关系的协程,可以通过共享 Context 来确保依赖链上的协程能够同步取消。如果一个协程超时,其依赖的协程也应该通过 Context 收到取消信号并停止。

2. 示例代码

package main

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

// task1 模拟一个任务,依赖于 task2 的结果
func task1(ctx context.Context, wg *sync.WaitGroup, resultChan chan<- string) {
    defer wg.Done()

    select {
    case <-ctx.Done():
        fmt.Println("task1: context cancelled, exiting")
        return
    case result := <-resultChan:
        fmt.Println("task1: received result from task2:", result)
        // 模拟一些工作
        time.Sleep(2 * time.Second)
        fmt.Println("task1: completed")
    }
}

// task2 模拟另一个任务
func task2(ctx context.Context, wg *sync.WaitGroup, resultChan chan<- string) {
    defer wg.Done()

    select {
    case <-ctx.Done():
        fmt.Println("task2: context cancelled, exiting")
        return
    default:
        // 模拟一些工作
        time.Sleep(1 * time.Second)
        resultChan <- "task2 result"
        fmt.Println("task2: completed")
    }
}

func main() {
    var wg sync.WaitGroup
    resultChan := make(chan string)

    // 创建带超时的Context
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel()

    // 启动 task1
    wg.Add(1)
    go task1(ctx, &wg, resultChan)

    // 启动 task2
    wg.Add(1)
    go task2(ctx, &wg, resultChan)

    // 等待所有任务完成
    go func() {
        wg.Wait()
        close(resultChan)
    }()

    // 等待超时或所有任务完成
    select {
    case <-ctx.Done():
        fmt.Println("main: overall timeout reached")
    case <-time.After(5 * time.Second):
        fmt.Println("main: all tasks completed within time")
    }
}

代码说明

  1. task1task2 函数:这两个函数分别模拟不同的任务。task1 依赖于 task2 的结果,通过 resultChan 接收 task2 的结果。在函数内部,使用 select 语句监听 ctx.Done() 信号,以便在 Context 取消时及时退出。
  2. main 函数
    • 使用 context.WithTimeout 创建一个3秒超时的 Context
    • 启动 task1task2 两个协程,并传递 Contextsync.WaitGroup
    • 使用 sync.WaitGroup 等待所有协程完成,并在所有协程完成后关闭 resultChan
    • 使用 select 语句监听 ctx.Done() 信号或等待5秒,以确保在超时或所有任务完成后程序退出。

通过这种方式,可以有效地使用 Context 管理并发协程的超时控制,并处理协程之间的依赖关系。