在线精品99_中国九九盗摄偷拍偷看_91免费版在线观看_91.app_91高清视频在线_99热最新网站

Go编程中recover源码是什么

167次阅读
没有评论

共计 3009 个字符,预计需要花费 8 分钟才能阅读完成。

本篇内容介绍了“Go 编程中 recover 源码是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让丸趣 TV 小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

recover 的真身

就像我们之前针对 panic 做的一样,我们也写一段简单的代码,通过其汇编码尝试找出内置函数 recover() 的底层实现。

编写以下简单的代码,并保存在名为 compile.go 的文件里:

// recover/compile.go
package recover
func compile() {defer func() {recover()
}

然后使用以下命令编译代码:

go tool compile -S recover/compile.go

接着根据代码行号找出 recover() 语句对应的汇编码:

0x0024 00036 (recover/compile.go:5) PCDATA $0, $1
0x0024 00036 (recover/compile.go:5) PCDATA $1, $0
0x0024 00036 (recover/compile.go:5) LEAQ  ..fp+40(SP), AX
0x0029 00041 (recover/compile.go:5) PCDATA $0, $0
0x0029 00041 (recover/compile.go:5) MOVQ AX, (SP)
0x002d 00045 (recover/compile.go:5) CALL runtime.gorecover(SB)

我们可以看到 recover() 函数调用被替换成了 runtime.gorecover() 函数。runtime.gorecover() 实现源码位于 src/runtime/panic.go。

gorecover()

runtime.gorecover() 函数实现很简短:

func gorecover(argp uintptr) interface{} {gp := getg()
 //  获取 panic 实例,只有发生了 panic,实例才不为 nil
 p := gp._panic
 // recover 限制条件
 if p != nil   !p.goexit   !p.recovered   argp == uintptr(p.argp) {
 p.recovered = true
 return p.arg
 return nil
}

短短的代码,蕴含的信息量却很大。它可以解释以下问题:

recover() 到底是如何恢复 panic 的?

为什么 recover() 一定要在 defer() 函数中才生效?

假如 defer() 函数中调用了函数 A(),为什么 A() 中的 recover() 不能生效?

恢复逻辑

runtime.gorecover() 函数通过协程数据结构中的_panic 得到当前的 panic 的实例(上面代码中 p),如果当前 panic 的状态支持 recover,给该 panic 实例标记 recovered 状态(p.recovered = true),最后返回 panic() 函数的参数(p.arg)。

另外,当前执行 recover() 的 defer 函数是被 runtime.gopanic() 执行的,defer 函数执行结束以后,runtime.gopanic() 函数中会检查 panic 实例的 recovered 状态,如果发现 panic 被恢复,runtime.gopanic() 将会结束当前 panic 流程,将程序流程恢复正常。

生效条件

通过代码的 if 语句可以看到需要满足四个条件才可以恢复 panic,且四个条件缺一不可:

p != nil:必须存在 panic;

!p.goexit:非 runtime.Goexit();

!p.recovered:panic 还未被恢复;

argp == uintptr(p.argp):recover() 必须被 defer() 直接调用。

当前协程没有产生 panic 时,协程结构体中 panic 的链表为空,不满足恢复条件。

当程序运行 runtime.Goexit() 时也会创建一个 panic 实例,会标记该实例的 goexit 属性为 true,但该类型的 panic 不能被恢复。

假设函数包含多个 defer 函数,前面的 defer 通过 recover() 消除 panic 后,函数中剩余的 defer 仍然会执行,但不能再次 recover(),如下代码所示,函数第一行 defer 中的 recover() 将返回 nil。

func foo() {defer func() {recover()}() //  恢复无效,因为_panic.recovered = true
 defer func() {recover()}() //  标记_panic.recovered = true
 panic(err)
}

细心的读者或许会发现,内置函数 recover() 没有参数,runtime.gorecover() 函数却有参数,为什么呢?这正是为了限制 recover() 必须被 defer() 直接调用。

runtime.gorecover() 函数的参数为调用 recover() 函数的参数地址,通常是 defer 函数的参数地址,同地_panic 实例中也保存了当前 defer 函数的参数地址,如果二者一致,说明 recover() 被 defer 函数直接调用。举例如下:

func foo() {defer func() { //  假设函数为 A
 func() { //  假设函数为 B
 // runtime.gorecover(B),传入函数 B 的参数地址
 // argp == uintptr(p.argp)  检测失败,无法恢复
 if err := recover(); err != nil { 
 fmt.Println(A)
}

设计思路

通过以上源码的分析,我们可以很好地回答以下问题了:

为什么 recover() 一定要在 defer() 函数中才生效?

假如 defer() 函数中调用了函数 A(),为什么 A() 中的 recover() 不能生效?

如果 recover() 不在 defer() 函数中,那么 recover() 可能出现在 panic() 之前,也可能出现在 panic() 之后,出现在 panic() 之前,因为找不到 panic 实例而无法生效,出现在 panic() 之后,代码没有机会执行,所以 recover() 必须存在于 defer 函数中才会生效。

通过上面的分析,从代码层面我们理解了为什么 recover() 函数必须被 defer 直接调用才会生效。但为什么要有这样的设计呢?

笔者也没有找到官方关于此设计的资料,不过笔者认为此设计非常合理。

考虑下面的代码:

func foo() {defer func() {thirdPartPkg.Clean() //  调用第三方包清理资源
 if err != nil { //  条件不满足触发 panic
 panic(xxx)
}

有时我们会在代码里显式地触发 panic,同时往往还会在 defer 函数里调用第三方包清理资源,如果第三方包也使用了 recover(),那么我们触发的 panic 将会被拦截,而且这种拦截可能是非预期的,并不我们期望的结果。

“Go 编程中 recover 源码是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注丸趣 TV 网站,丸趣 TV 小编将为大家输出更多高质量的实用文章!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-08-04发表,共计3009字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)
主站蜘蛛池模板: xart在线 | 一本一道人人妻人人妻αv 一本一道色欲综合网中文字幕 | 成人a视频高清在线观看 | 99玖玖爱视频在线观看 | a级毛片在线免费 | 国模大尺度啪啪 | 国产一区二区三区四区五区 | 亚洲男人第一av网站 | 一区二区三区在线观看视频 | 亚洲精品成a人在线观看 | 国产一区二区精品久久91 | 免费激情网站 | 在线91精品亚洲网站精品成人 | 99re在线视频观看 | 国产一区二区三区在线观看视频 | 国产精品密播放国产免费看 | 黄片毛片免费观看 | 深夜福利小视频在线观看 | 亚洲国产精品a一区二区三区 | 男人的天堂日本 | 国产一级aa大片毛片 | 国产做无码视频在线观看 | 久久久久久夜精品精品免费 | 国产美女主播一级成人毛片 | 亚洲a影院 | 中文国产成人精品久久96 | 精品人妻va出轨中文字幕 | 国产精品一区不卡 | 国产一起色一起爱 | 日本一区二区精品88 | 福利视频国产 | 国产综合无码一区二区色蜜蜜 | 免费看一级黄色片 | 国内自拍网红在综合图区 | 色视频线观看在线网站 | 国产精品久久久一区二区三区 | 亚洲视频网址 | 国产精品va在线播放我和闺蜜 | 九九热精品视频在线播放 | 亚洲国产精品自在现线让你爽 | m3u8久久国产精品影院 |