panic和recover的实现原理

panic和recover


panic

panic的数据结构

type _panic struct{
	argp unsafe.Pointer //defer的参数空间地址
	arg interface //panic的参数
	link *_panic //链接之前的panic
	recovered bool//是否被恢复
	aborted bool //是否被终止
}

同defer一样,每个goroutine数据结构中实际上也有一个panic链表指针,该指针指向一个panic的单链表,每次执行一个panic时就将panic插入到单链表表头

panic单链表由一个个_panic结构体组成
在这里插入图片描述
案例

func A1(){
	...
	panic("panicA1")
	...
func A(){
	defer A1()
	defer A2()
	panic("panicA")
	.....
}

A中执行到panic时,defer链表中已经注册了A1和A2,panic后面的代码将不被执行
在这里插入图片描述
panic执行defer时会先把defer结构体中的start置为true,标记它已经开始执行,并且把_panic字段指向当前的panic,表示这个defer由这个panic触发,如果函数A2能正常结束,A2就会被移除,继续执行下一个defer

假设A2正常结束,执行defer A1()前,在defer链表A1中的start和_panic字段进行标记,当执行到A1函数中的panicA1时,panicA1成为当前执行的panic如图:
在这里插入图片描述
panicA1成为当前执行的panic时,defer链表A1已经执行,并且触发它·执行的并不是panicA1,而是panicA

执行完后,panicA被终止,defer链表A1被移除
在这里插入图片描述

注意:打印异常信息时,是从panic链表尾开始打印的,所以先打印panicA,再打印panicA1

没有recover的发生时panic的处理逻辑就是这样,这里关键点有两个第一个是panic执行defer函数的方式,先标记后释放,目的就是终止之前发生的panic,如案例中的panicA。第二个是异常信息的处理方式,先panic的先打印。

recover

recover就是把当前执行panic置为恢复,也就是把panic结构中的recover字段置为true

案例

func A(){
	defer A1()
	defer A2()
	panic("panicA")
}
func A2(){
	p := recover()
	fmt.Println(p)
	panic("panicA1")
}

执行到panicA2时,它成为当前执行的panic,然后执行defer链表,但defer链表中的A2已经执行,所以把panicA给终止,并把A2从defer链表中移除
在这里插入图片描述
去除defer链表中的A2后,因为当前执行的panic为panicA2,所以A1中的_panic被标记为panicA2
在这里插入图片描述

A1结束后被移除,defer链表为空,接下来就要输出异常信息了

注意:对于已经recover的panic,打印时会加上recover标记

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值