MST

星途 面试题库

面试题:Go中多个层级协程间Context传递与控制

假设有一个复杂的任务调用链,存在多层级的协程嵌套调用(例如A协程调用B协程,B协程调用C协程等)。请设计并实现一个方案,在这个调用链中有效地传递Context,使得无论在哪个层级取消Context,整个调用链上的所有协程都能收到取消信号并进行优雅关闭,同时说明如何处理不同层级协程间可能存在的资源清理等问题。
12.2万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
  1. 设计思路
    • 使用context.Context来管理取消信号。context.Context是Go语言中用于传递截止时间、取消信号和其他请求范围值的对象。
    • 在每一层协程调用时,将context.Context作为参数传递下去。这样,当顶层的context被取消时,所有下层协程都能接收到取消信号。
  2. 实现代码示例(以Go语言为例)
package main

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

// C协程
func CoroutineC(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("CoroutineC收到取消信号,开始清理资源并退出")
            // 这里进行资源清理,例如关闭文件、数据库连接等
            return
        default:
            fmt.Println("CoroutineC正在执行任务")
            time.Sleep(100 * time.Millisecond)
        }
    }
}

// B协程
func CoroutineB(ctx context.Context) {
    ctx, cancel := context.WithCancel(ctx)
    defer cancel()
    go CoroutineC(ctx)
    for {
        select {
        case <-ctx.Done():
            fmt.Println("CoroutineB收到取消信号,开始清理资源并退出")
            // 这里进行资源清理,例如关闭文件、数据库连接等
            return
        default:
            fmt.Println("CoroutineB正在执行任务")
            time.Sleep(100 * time.Millisecond)
        }
    }
}

// A协程
func CoroutineA(ctx context.Context) {
    ctx, cancel := context.WithCancel(ctx)
    defer cancel()
    go CoroutineB(ctx)
    for {
        select {
        case <-ctx.Done():
            fmt.Println("CoroutineA收到取消信号,开始清理资源并退出")
            // 这里进行资源清理,例如关闭文件、数据库连接等
            return
        default:
            fmt.Println("CoroutineA正在执行任务")
            time.Sleep(100 * time.Millisecond)
        }
    }
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
    defer cancel()
    go CoroutineA(ctx)
    time.Sleep(1000 * time.Millisecond)
}
  1. 不同层级协程资源清理处理
    • 在每一层协程接收到ctx.Done()信号后,首先进行本层的资源清理工作。例如,如果协程打开了文件,需要调用file.Close()关闭文件;如果连接了数据库,需要调用db.Close()关闭数据库连接等。
    • 清理完成后,通过defer cancel()(如果有创建子context)来取消下层协程的context,确保下层协程也能收到取消信号并进行资源清理。这样,通过层层传递取消信号和清理资源,就能实现整个调用链的优雅关闭。