Go 的异常处理
前言
Go 语言追求简洁优雅,所以,Go 语言不支持传统的 try … catch … finally 这种异常,因为 Go 语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱。因为开发者很容易滥用异常,甚至一个小小的错误都抛出一个异常。在 Go 语言中,使用多值返回来返回错误。不要用异常代替错误,更不要用来控制流程。在极个别的情况下,也就是说,遇到真正的异常的情况下(比如除数为 0 了)。才使用 Go 中引入的 Exception 处理:defer, panic, recover。
这几个异常的使用场景可以这么简单描述:Go 中可以抛出一个 panic 的异常,然后在 defer 中通过 recover 捕获这个异常,然后正常处理。
代码示例
package main
import "fmt"
func main() {
// 必须要先声明 defer,否则不能捕获到 panic 异常
// 解释:出现异常后直接跳过 return,执行 defer 代码
defer func() {
fmt.Println("d")
if err := recover(); err != nil {
fmt.Println(err)
}
fmt.Println("e")
}()
fmt.Println("a")
panic(55)
fmt.Println("b")
fmt.Println("c")
}
输出:
a
d
55
e
defer
defer 的意思是延迟,意为其后的代码延迟执行,在 Go 中,defer 的代码将在 panic 和 return 后执行;类似于 C++的析构函数,在函数结束后执行,但 defer 语句是动态的,在上面那个例子中,如果 panic 出现在 defer 之前,defer 语句并未被执行也就没有相应的错误处理。
defer 的三大特性:
-
defer 语句声明时,其参数立即求值
A deferred function’s arguments are evaluated when the defer statement is evaluated
-
defer 函数在其后代码执行完之后按照先进后出的顺序执行
Deferred function calls are executed in Last In First Out order after the surrounding function returns.
-
defer 语句可读取并修改函数的命名返回值
Deferred functions may read and assign to the returning function’s named return values.
defer 的其他使用场景:
-
释放一个信号量
mu.Lock() defer mu.Unlock()
-
输出底部信息
printHeader() defer printFooter()
panic
Panic 是一个内置的函数,它停止普通的控制流并开始恐慌。当函数 F 调用 panic 时,F 的执行停止,F 中的任何延迟函数被正常执行,然后 F 返回给它的调用者。对于调用者来说,F 的行为就像对 panic 的调用。这个过程继续在堆栈中进行,直到当前 goroutine 中的所有函数都返回,这时程序就会崩溃。除非在 defer 函数中调用了 recover 函数处理了 panic,这时恐慌将不会继续向上传递。恐慌可以通过直接调用 panic 来启动。它们也会由运行时错误引起,如越界数组访问。
recover
recover 是一个内置的函数,可以重新获得对一个恐慌的 goroutine 的控制。Recover 只在延迟函数中有用。在正常执行过程中,对 recover 的调用将返回 nil,没有其他影响。如果当前的 goroutine 处于恐慌状态,对 recover 的调用将捕获给定的恐慌值并恢复正常执行。