写点什么

Go 学习笔记之 Panic 异常

发布于: 4 小时前
Go 学习笔记之 Panic异常

Go 的类型系统会在编译时捕获很多错误,但有些错误只能在运行时检查,如数组访问越界、空指针引用等。这些运行时错误会引起 painc 异常。

当 panic 异常发生时,程序会中断运行,并立即执行在该 goroutine 中被延迟的函数(defer 机制)。随后,程序崩溃并输出日志信息。

日志信息包括 panic value 和函数调用的堆栈跟踪信息。

panic value 通常是某种错误信息。对于每个 goroutine,日志信息中都会有与之相对的,发生 panic 时的函数调用堆栈跟踪信息。

通常,我们不需要再次运行程序去定位问题,日志信息已经提供了足够的诊断依据。

引发 Panic

不是所有的 panic 异常都来自运行时,直接调用内置的 panic 函数也会引发 panic 异常;panic 函数接受任何值作为参数。当某些不应该发生的场景发生时,我们就应该调用 panic。

比如,当程序到达了某条逻辑上不可能到达的路径:

switch s := suit(drawCard()); s {case "Spades":                                // ...case "Hearts":                                // ...case "Diamonds":                              // ...case "Clubs":                                 // ...default:    panic(fmt.Sprintf("invalid suit %q", s)) // Joker?}
复制代码

适用场景

虽然 Go 的 panic 机制类似于其他语言的异常,但 panic 的适用场景有一些不同。

由于 panic 会引起程序的崩溃,因此 panic 一般用于严重错误,如程序内部的逻辑不一致。

任何崩溃都表明代码中存在漏洞,所以对于大部分漏洞,我们应该使用 Go 提供的错误机制,而不是 panic,尽量避免程序的崩溃。在健壮的程序中,任何可以预料到的错误,如不正确的输入、错误的配置或是失败的 I/O 操作都应该被优雅的处理,最好的处理方式,就是使用 Go 的错误机制。

Recover 捕获异常

我们可以从异常中恢复,至少我们可以在程序崩溃前,做一些操作。

举个例子,当 web 服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭;如果不做任何处理,会使得客户端一直处于等待状态。如果 web 服务器还在开发阶段,服务器甚至可以将异常信息反馈到客户端,帮助调试。


如果在 deferred 函数中调用了内置函数 recover,并且定义该 defer 语句的函数发生了 panic 异常,recover 会使程序从 panic 中恢复,并返回 panic value。导致 panic 异常的函数不会继续运行,但能正常返回。在未发生 panic 时调用 recover,recover 会返回 nil。

deferred 函数帮助 Parse 从 panic 中恢复。在 deferred 函数内部,panic value 被附加到错误信息中;并用 err 变量接收错误信息,返回给调用者。我们也可以通过调用 runtime.Stack 往错误信息中添加完整的堆栈调用信息。

func Parse(input string) (s *Syntax, err error) {    defer func() {        if p := recover(); p != nil {            err = fmt.Errorf("internal error: %v", p)        }    }()    // ...parser...}
复制代码


发布于: 4 小时前阅读数: 3
用户头像

坚持分享接地气儿的架构技术文章! 2018.02.26 加入

同名微信公众号「架构精进之路」,专注软件架构研究,技术学习与职业成长!坚持原创总结、沉淀和分享,希望能带给大家一些引导和启发,感谢各位的支持(关注、点赞、分享)!

评论

发布
暂无评论
Go 学习笔记之 Panic异常