2

goroutine配上panic会怎样?

 2 years ago
source link: https://zacharyfan.com/archives/1556.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

goroutine配上panic会怎样?

这里是Z哥的个人公众号

每周五11:45 按时送达

当然了,也会时不时加个餐~

我的第「220」篇原创敬上

大家好,我是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 的正确姿势是什么呢?

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

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


原创文章,转载请注明本文链接: https://zacharyfan.com/archives/1556.html

关于作者:张帆(Zachary,个人微信号:Zachary-ZF)。坚持用心打磨每一篇高质量原创。欢迎扫描二维码~

微信公众号

定期发表原创内容:架构设计丨分布式系统丨产品丨运营丨一些思考。

如果你是初级程序员,想提升但不知道如何下手。又或者做程序员多年,陷入了一些瓶颈想拓宽一下视野。欢迎关注我的公众号「跨界架构师」,回复「技术」,送你一份我长期收集和整理的思维导图。

如果你是运营,面对不断变化的市场束手无策。又或者想了解主流的运营策略,以丰富自己的“仓库”。欢迎关注我的公众号「跨界架构师」,回复「运营」,送你一份我长期收集和整理的思维导图。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK