panic是panic
fatal是fatal
先看一段重复Unlock
的例子
package main
import (
"fmt"
"sync"
)
func main() {
defer func() {
if err := recover(); err != nil {
fmt.Println("打印错误:",err) // 捕获并打印
}
}()
l := sync.Mutex{}
l.Lock()
l.Unlock()
l.Unlock() // 重复解锁
}
输出
fatal error: sync: unlock of unlocked mutex
可见defer+recover
是无法捕获fatal error
地,我们的打印没有执行。为啥呢,一步步来~
首先,让我们全局搜索一下sync: unlock of unlocked mutex
,发现了这段文案的出处。
// path: /usr/local/go/src/sync/mutex.go:196
func (m *Mutex) unlockSlow(new int32) {
if (new+mutexLocked)&mutexLocked == 0 {
throw("sync: unlock of unlocked mutex") // 错误文案在这里
}
...忽略部分...
但是我们发现并没有错误前缀fatal error:
继续查看throw()
函数,我们发现了错误前缀fatal error:
。
// path: /usr/local/go/src/runtime/panic.go:1188
func throw(s string) {
// Everything throw does should be recursively nosplit so it
// can be called even when it's unsafe to grow the stack.
// 把错误加入到系统调用栈中
systemstack(func() {
print("fatal error: ", s, "\n")
})
...忽略部分...
fatalthrow()
*(*int)(nil) = 0 // not reached 这一段是为了保险起见,让进程能退出。
}
再继续查看fatalthrow()
// path: /usr/local/go/src/runtime/panic.go:1244
func fatalthrow() {
...忽略部分...
systemstack(func() {
...忽略部分...
exit(2) // 关键,进程直接退出。
})
*(*int)(nil) = 0 // not reached 这一段是为了保险起见,让进程能退出。
}
无法捕获的原因就在于这个exit(2)
函数了 。
exit(2)
:执行该函数会直接退出进程,所以我们的defer+recover
无法执行了。
总结
throw()
函数会打印调用栈信息,然后直接终止整个进程!
想知道其他fatal error
,全局搜索throw()
函数,看都有哪些地方调用即可。
出个面试题:go都有哪些异常捕获不住?
本文属于原创,首发于微信公众号【小易哥学呀学】,如需转载请后台留言。
加微信入Golang交流群。vx:17610015120
网友评论