MST

星途 面试题库

面试题:Go中Context取消机制在并发场景的应用

假设你正在开发一个涉及多个并发任务的Go程序,这些任务可能需要在特定条件下全部取消。请描述如何使用Context取消机制来优雅地实现这一需求,并举例说明可能出现的问题及解决方案。
17.2万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

使用Context取消机制实现并发任务取消

  1. 创建Context: 在Go中,通常使用context.Background()作为根Context,然后通过context.WithCancelcontext.WithTimeoutcontext.WithDeadline创建带有取消功能的Context。例如,使用context.WithCancel创建可取消的Context:
package main

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

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
  1. 传递Context: 将创建的Context传递给需要取消的并发任务函数。任务函数应定期检查Context的取消信号。例如:
    go func(ctx context.Context) {
        for {
            select {
            case <-ctx.Done():
                fmt.Println("任务被取消")
                return
            default:
                fmt.Println("任务正在执行")
                time.Sleep(1 * time.Second)
            }
        }
    }(ctx)
  1. 取消任务: 在需要取消任务的地方调用取消函数cancel()。例如,在主函数中等待3秒后取消任务:
    time.Sleep(3 * time.Second)
    cancel()
    time.Sleep(1 * time.Second)
}

可能出现的问题及解决方案

  1. 未正确传递Context
    • 问题:如果Context没有正确传递到所有需要取消的函数中,部分任务将无法被取消。
    • 解决方案:确保在启动每个并发任务时,都将Context作为参数传递进去。例如在上述代码中,go func(ctx context.Context)这样的形式确保了Context被传递到任务函数中。
  2. 任务取消不及时
    • 问题:如果任务函数没有定期检查ctx.Done()通道,任务可能不会及时响应取消信号。
    • 解决方案:在任务函数中,通过select语句定期检查ctx.Done()通道,如上述代码中select { case <-ctx.Done():... }这样的结构,确保任务能够及时响应取消信号。
  3. 资源未正确清理
    • 问题:任务取消后,可能存在一些资源(如文件句柄、网络连接等)没有被正确关闭或清理。
    • 解决方案:在任务被取消后,添加相应的资源清理代码。例如,如果任务打开了一个文件,在检测到取消信号后,应关闭文件:
    go func(ctx context.Context) {
        file, err := os.Open("example.txt")
        if err != nil {
            fmt.Println("打开文件错误:", err)
            return
        }
        defer file.Close()

        for {
            select {
            case <-ctx.Done():
                fmt.Println("任务被取消,关闭文件")
                return
            default:
                // 任务逻辑
                time.Sleep(1 * time.Second)
            }
        }
    }(ctx)