2025/08/072025/08/07技术约 1 分钟443 字

go中的panic和recover

  1. panic 是 go 的内置机制,输出严重错误。如果没有恢复程序会打印堆栈,然后退出
  2. recover 也是 go 的内置机制,在当前 goroutine 恢复 panic,返回值为 panic 中的引用信息

注意: recover 一定要与 defer 一起使用,不然 recover 只会返回 nil

示例程序:

package main

func A() {
	B()
}

func B() {
	C()
}

func C() {
	panic("报错了")
}

func main() {
	A()
}

输出结果:

panic: 报错了

goroutine 1 [running]:
main.C(...)
        /code/demo/panic/main.go:12
main.B(...)
        /code/demo/panic/main.go:8
main.A(...)
        /code/demo/panic/main.go:4
main.main()
        /code/demo/panic/main.go:16 +0x25
exit status 2

对应的报错堆栈信息比较清晰 C -> B -> A -> main

最后抛到 main 函数

我们可以在 main 调用 A 函数前使用 defer+recover 对 panic 进行捕获

defer func() {
    if r := recover(); r != nil {
        fmt.Println("捕获到异常", r)
    }
}()
A()

如上述代码所示,这样就能成功捕获错误,输出如下

捕获到异常 报错了

recover 只能用于当前 goroutine,我们来试试在子 goroutine 处 panic,main 函数是否能捕获到

package main

import (
	"fmt"
	"sync"
)

func doSomething() {
	defer wg.Done()
	fmt.Println("准备panic")
	panic("子goroutine报错了")
}

var wg sync.WaitGroup

func main() {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("recover捕获到异常值为:", r)
		}
	}()
	wg.Add(1)
	go doSomething()
	wg.Wait()
	fmt.Println("无法捕获")
}

输出结果:

root@pzj:/code/demo/panic# go run main.go
准备panic
panic: 子goroutine报错了

goroutine 6 [running]:
main.doSomething()
        /code/demo/panic/main.go:11 +0x90
created by main.main in goroutine 1
        /code/demo/panic/main.go:18 +0x31
exit status 2

可以看到子 goroutine 直接 panic 了,而主 goroutine 捕获是没有用的,查看输出

发现无法捕获没有输出,说明在子 goroutine panic 后直接就退出了,根本就不会回到 main,所以 recover 只能恢复当前 goroutine 的 panic,无法恢复其他 goroutine 的

评论区