You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go协程中Panic的捕获与恢复问题咨询

如何恢复Go协程中产生的Panic?

你碰到的这个问题其实是Go语言里goroutine和recover机制的核心特性导致的:recover只能捕获当前goroutine内部触发的panic,每个goroutine都是独立的执行流,main函数里的defer recover完全管不到其他goroutine里的panic。

你看,当直接调用handle(done)时,这个函数是在main goroutine里执行的,所以panic发生后,main里的defer recover能正常捕获;但用go handle(done)启动时,handle跑在一个全新的goroutine里,main的recover根本感知不到这个新goroutine里的panic,自然无法恢复。

解决方法

1. 在目标goroutine内部添加recover逻辑

最直接的方式就是在handle函数里自己加defer recover,捕获自身的panic:

func main() {
    done := make(chan int64)
    defer fmt.Println("Graceful End of program")

    go handle(done)
    for {
        select {
        case <-done:
            return
        }
    }
}

func handle(done chan int64) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from panic in handle goroutine:", r)
            // 可以在这里处理panic,比如发送结束信号给main
            done <- 0
        }
    }()
    var a *int64
    a = nil
    fmt.Println(*a)
    done <- *a
}

这样,handle里的panic会被自身的defer recover捕获,还能通过channel通知main函数协程已经处理完panic并退出。

2. 封装通用的goroutine启动函数

如果你的项目里有很多需要捕获panic的goroutine,可以封装一个通用的启动函数,减少重复代码:

func safeGo(f func()) {
    go func() {
        defer func() {
            if r := recover(); r != nil {
                fmt.Printf("Recovered panic: %v\n", r)
                // 这里可以添加统一的panic日志、监控等逻辑
            }
        }()
        f()
    }()
}

// 使用方式
func main() {
    done := make(chan int64)
    defer fmt.Println("Graceful End of program")

    safeGo(func() {
        handle(done)
    })

    for {
        select {
        case <-done:
            return
        }
    }
}

func handle(done chan int64) {
    var a *int64
    a = nil
    fmt.Println(*a)
    done <- *a
}

3. 传递panic信息到主goroutine

如果需要让main函数知道goroutine里发生了panic,可以用一个额外的channel来传递panic信息:

func main() {
    done := make(chan int64)
    panicChan := make(chan interface{})
    defer fmt.Println("Graceful End of program")

    go func() {
        defer func() {
            if r := recover(); r != nil {
                panicChan <- r
            }
        }()
        handle(done)
    }()

    for {
        select {
        case <-done:
            return
        case p := <-panicChan:
            fmt.Println("Received panic from goroutine:", p)
            // 在这里处理panic后的逻辑,比如退出程序或继续执行
            return
        }
    }
}

func handle(done chan int64) {
    var a *int64
    a = nil
    fmt.Println(*a)
    done <- *a
}

关键总结

  • 每个goroutine的panic只能由自身的recover捕获,跨goroutine无效
  • 处理goroutine panic的核心思路是:在goroutine内部添加recover逻辑,或者通过channel把panic信息传递到主协程处理

内容的提问来源于stack exchange,提问作者Mohit Jain

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:26:22