MST

星途 面试题库

面试题:Go并发程序模块化设计的错误处理与恢复

在一个复杂的Go并发程序中,各个模块相互协作,当某个模块内部的goroutine发生错误时,如何设计一个通用的错误处理和恢复机制,确保错误能被正确捕获并传递到合适的上层模块进行处理,同时保证整个并发程序的稳定性和健壮性?请阐述设计思路并给出相关代码框架。
32.9万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 错误传递
    • 使用 context.Context 来传递取消信号和携带值,它可以贯穿整个 goroutine 树,当子 goroutine 发生错误时,可以通过 context 取消整个相关的 goroutine 链。
    • 利用通道(channel)在不同 goroutine 之间传递错误信息。一个 goroutine 发生错误时,将错误发送到特定的通道,上层模块从该通道接收错误。
  2. 恢复机制
    • 在 goroutine 内部使用 deferrecover 来捕获未处理的 panic,避免整个程序崩溃。捕获到 panic 后,可以将其转换为错误信息并通过通道传递出去。

代码框架

package main

import (
    "context"
    "fmt"
)

// 模拟一个可能出错的模块
func module(ctx context.Context, errCh chan<- error) {
    defer func() {
        if r := recover(); r != nil {
            err := fmt.Errorf("recovered from panic: %v", r)
            errCh <- err
        }
    }()

    // 模拟业务逻辑
    select {
    case <-ctx.Done():
        return
    default:
        // 这里模拟一个可能发生的错误
        if someCondition {
            err := fmt.Errorf("module internal error")
            errCh <- err
            return
        }
    }
}

func main() {
    errCh := make(chan error)
    ctx, cancel := context.WithCancel(context.Background())

    go module(ctx, errCh)

    go func() {
        // 这里可以添加其他 goroutine 的启动

        // 等待错误发生或者所有 goroutine 完成
        for i := 0; i < numGoroutines; i++ {
            select {
            case err := <-errCh:
                fmt.Printf("Received error: %v\n", err)
                cancel() // 取消所有相关的 goroutine
            }
        }
        close(errCh)
    }()

    // 主 goroutine 可以做其他事情
    //...

    // 等待所有事情完成
    select {}
}

在上述代码框架中:

  • module 函数模拟一个可能出错的模块,它使用 deferrecover 捕获 panic 并转换为错误。
  • main 函数中启动了 module goroutine,并通过 errCh 通道接收错误。当接收到错误时,通过 cancel 函数取消所有相关的 goroutine。
  • 主 goroutine 可以在等待错误的同时做其他事情,并且通过一个无限的 select {} 等待所有任务完成。