Goroutine 配上 Panic会怎样?

开发 前端
goroutine 实现了 M:N 的线程模型,是协程的一种实现。golang 内置的调度器,可以让多核 CPU 中每个 CPU 执行一个协程。

大家好,我是Z哥。

最近用 Golang 进行编码也有3个月了,说来惭愧,到现在还没正儿八经深入学习一下 Golang,一直被工作赶着往前在跑。

最近正好在工作中遇到一个问题,需要对 Golang 中的 goroutine 和 panic & recover 稍做深入的了解,算是忙里偷闲学习一下。

对 goroutine 的底层细节就不展开了,网上有不少相关的文章解读,如果你愿意的话,也可以去扒一下 Golang 的源码。

简单对 goroutine 进行一下概括就是:

goroutine 实现了 M:N 的线程模型,是协程的一种实现。golang 内置的调度器,可以让多核 CPU 中每个 CPU 执行一个协程。

单从表现来看,你可以将 goroutine 看作是 java 之类编程语言中的多线程的运行效果。

好了,那么问题来了:goroutine 中发生 panic 会怎样?

话不多说,实践是检验真理的唯一标准,我们直接上手 coding。

func main()  {
go panicInGoroutine()

//以下3行代码是为了让控制台挂起,等待gorouine运行完毕。
fmt.Println("wait")
input := bufio.NewScanner(os.Stdin)
input.Scan()
}

func panicInGoroutine() {
panic("panic in goroutine.")
}

运行代码的结果如下:

可以看到,整个程序都崩了。

那么,如果在 goroutine 里的 goroutine 发出 panic 呢?也是一样的效果,程序崩了。

可能你会觉得整个程序之所以会崩,是因为异常被层层上抛到主线程导致的,其实并非如此。在 Golang 中,任何地方发生的任意一个 panic,都会直接程序退出。

那么怎么才能让程序不退出呢?

通过调用 recover() 方法来捕获 panic 并恢复将要崩掉的程序。

func main() {
go panicInGoroutine()

//以下3行代码是为了让控制台挂起,等待gorouine运行完毕。
fmt.Println("wait")
input := bufio.NewScanner(os.Stdin)
input.Scan()
}

func panicInGoroutine() {
//recover()必须要和defer配合一起用,确保一旦执行到该方法体,这里定义的defer方法一定会被执行,哪怕是发生了panic。
defer func() {
err := recover()
if err != nil {
fmt.Printf("recover receive a err: %+v \n", err)
}
}()

panic("panic in goroutine.")
}

执行上面的代码,结果如下:

可以看到,程序没有再崩了。那么新的问题又来了,能不能把 recover() 放到最外层的方法里,这样可以更好地实现一次 recover() 覆盖当前方法其下所有的 panic。

func main() {
defer func() {
err := recover()
if err != nil {
fmt.Printf("recover receive a err: %+v \n", err)
}
}()

go panicInGoroutine()

//以下3行代码是为了让控制台挂起,等待gorouine运行完毕。
fmt.Println("wait")
input := bufio.NewScanner(os.Stdin)
input.Scan()
}

func panicInGoroutine() {
panic("panic in goroutine.")
}

运行之后的结果:

竟然还是崩了。如果你是一位 Java 或者 .Net 的程序员习惯了 try-catch-finally 的运行效果肯定对这个结果比较意外。在父方法定义的 recover() 竟然无法捕获到子方法里的 panic。

其实这里的原因是,外层方法中定义的 recover() 无法捕获通过 goroutine 执行的子方法中抛出的 panic。在上面的代码中,我们把 go panicInGoroutine() 前面的 go 去掉就可以正常捕获了。

好了,那么根据以上这些信息得到的处理 panic 的正确姿势是什么呢?

  • 必须通过 defer 关键字来调用 recover()。
  • 当通过 goroutine 调用某个方法,一定要确保内部有 recover() 机制。

如果你想进一步深入了解 panic 和 recove r的机制,分享你一个超棒的硬核视频:https://www.bilibili.com/video/BV155411Y7XT,第一遍看可能会有点晕,建议反复看,直到完全理解其原理。


责任编辑:武晓燕 来源: 跨界架构师
相关推荐

2021-09-09 09:46:25

Goroutine 函数runtime

2024-01-31 12:34:16

panic错误检测recover

2013-08-20 09:48:59

2014-12-31 10:02:14

Android可穿戴设备世界

2015-01-05 10:26:14

Android手机厂商

2016-10-21 09:45:20

RustFedoraJava

2010-08-20 13:53:20

2015-11-19 14:47:33

富兰克林编程

2022-02-22 11:41:06

数据泄露勒索软件

2021-03-12 08:53:09

GC调度Goroutine

2021-10-29 19:00:30

监控系统数据库

2014-02-19 16:26:26

VDI部署

2015-06-30 15:18:04

2013-07-29 17:04:18

2009-12-03 13:32:04

Virtuozzo捆绑

2021-11-02 06:58:52

移位负数二进制

2014-10-22 10:15:21

赛门铁克拆分安全公司

2022-11-24 11:09:03

自然语言处理(智能语音

2013-03-08 10:07:20

GO语言Goroutine

2023-11-20 22:55:00

Goroutine调度器
点赞
收藏

51CTO技术栈公众号