5.panic与recover

文章介绍了在Go语言编程中,使用协程时需要注意panic和recover的使用,以防止服务端程序因未捕获的panic而退出。通过示例代码展示了如何在子协程中正确使用defer和recover来捕获并处理panic,以及panic如何影响协程和父协程的执行流程。
摘要由CSDN通过智能技术生成

工作中,为了提高代码的性能,使用协程去处理相关逻辑的代码是随处可见的,但是有一点需要特别注意的就是,子协程中引发的panic,如果没有recover的话,是会引起整个服务端程序进程退出的。所以有一条准则,在每一个子协程内部,尽量都使用一下recover。又因为recover 是需要和defer搭配使用的,所以一般会在可能引发panic的代码前面,使用deferrecover

panic出现后,代码行为如下

  1. 逆序执行当前goroutinedefer链条(recover也正是从某个defer中介入的)
  2. recover的作用是,当程序出现panic时,recover能将其捕获,若捕获到 panic,则recover会返回error
  3. 若当前协程将panic捕获,则返回到上一级协程,如果panic没有被当前函数堆栈内的任一defer中的recover捕获,此时打印错误信息和调用堆栈,然后调用exit(2)结束整个进程
package main

import (
	"fmt"
	"time"
)

func divide(a, b int) int {
	defer func() {
		if err := recover(); err != nil {
			fmt.Printf("捕获了panic,err:%v\n", err)
		}
	}()

	res := a / b

	fmt.Println("出现panic后,即使panic被捕获了,因为已经执行了defer,这里的代码执行不到了")
	return res
}

func main() {

	go divide(9, 0)

	time.Sleep(5 * time.Second)
	fmt.Println("因为子协程中的panic被捕获了,所以程序并没有退出,还能正常执行")
}

执行结果:
在这里插入图片描述

通过观察输出,就能知道运行流程如何了,有一点 需要注意的是,recover不能直接跟在defer后面,而是需要使用defer后跟匿名函数的方式,将recover放到匿名函数中,如果不这样做,会编译报错。

如果将recover相关代码注释掉,则整个程序会因为没有捕获panic而异常退出,如下

package main

import (
	"fmt"
	"time"
)

func divide(a, b int) int {
	//defer func() {
	//	if err := recover(); err != nil {
	//		fmt.Printf("捕获了panic,err:%v\n", err)
	//	}
	//}()

	res := a / b

	fmt.Println("出现panic后,即使panic被捕获了,因为已经执行了defer,这里的代码执行不到了")
	return res
}

func main() {

	go divide(9, 0)

	time.Sleep(5 * time.Second)
	fmt.Println("因为子协程中的panic被捕获了,所以程序并没有退出,还能正常执行")
}

在这里插入图片描述
从如下代码可以看出,子协程中的panic不会传递给父协程,子协程内没有捕获,就直接panic

package main

import (
	"fmt"
	"time"
)

func divide(a, b int) int {
	//defer func() {
	//	if err := recover(); err != nil {
	//		fmt.Printf("捕获了panic,err:%v\n", err)
	//	}
	//}()

	res := a / b

	fmt.Println("出现panic后,即使panic被捕获了,因为已经执行了defer,这里的代码执行不到了")
	return res
}

func main() {
	defer func() {
		fmt.Println("defer")
		if err := recover(); err != nil {
			fmt.Printf("捕获了panic,err:%v\n", err)
		}
	}()
	go divide(9, 0)

	time.Sleep(5 * time.Second)
	fmt.Println("因为子协程中的panic被捕获了,所以程序并没有退出,还能正常执行")
}

输出:
在这里插入图片描述
可以看到main方法中的defer都没有执行到就panic退出了

如果子协程内部捕获了panic,则main方法中的defer是会正常执行的,如下:

package main

import (
	"fmt"
	"time"
)

func divide(a, b int) int {
	defer func() {
		if err := recover(); err != nil {
			fmt.Printf("捕获了panic,err:%v\n", err)
		}
	}()

	res := a / b

	fmt.Println("出现panic后,即使panic被捕获了,因为已经执行了defer,这里的代码执行不到了")
	return res
}

func main() {
	defer func() {
		fmt.Println("defer")
		if err := recover(); err != nil {
			fmt.Printf("捕获了panic,err:%v\n", err)
		}
	}()
	go divide(9, 0)

	time.Sleep(5 * time.Second)
	fmt.Println("因为子协程中的panic被捕获了,所以程序并没有退出,还能正常执行")
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值