golang的log.Fatal()和panic()函數的區別
在講兩者區別之前我們先看一下os.Exit()函數的定義:
func Exit(code int)Exit causes the current program to exit with the given status code.Conventionally, code zero indicates success, non-zero an error.The program terminates immediately; deferred functions are not run.
注意兩點:
- 應用程式馬上退出。
- defer函數不會執行。
再來看log.Fatal函數定義
func Fatal(v ...interface{})Fatal is equivalent to Print() followed by a call to os.Exit(1).
看原始碼:go/src/log/log.go
// Fatal is equivalent to l.Print() followed by a call to os.Exit(1).func (l *Logger) Fatal(v ...interface{}) { l.Output(2, fmt.Sprint(v...)) os.Exit(1)}
總結起來log.Fatal函數完成:
- 列印輸出內容
- 退出應用程式
- defer函數不會執行
和os.Exit()相比多了第一步。
再來看內建函數panic()函數定義:
// The panic built-in function stops normal execution of the current// goroutine. When a function F calls panic, normal execution of F stops// immediately. Any functions whose execution was deferred by F are run in// the usual way, and then F returns to its caller. To the caller G, the// invocation of F then behaves like a call to panic, terminating G's// execution and running any deferred functions. This continues until all// functions in the executing goroutine have stopped, in reverse order. At// that point, the program is terminated and the error condition is reported,// including the value of the argument to panic. This termination sequence// is called panicking and can be controlled by the built-in function// recover.func panic(v interface{})
注意幾點:
- 函數立刻停止執行 (注意是函數本身,不是應用程式停止)
- defer函數被執行
- 返回給調用者(caller)
- 調用者函數假裝也收到了一個panic函數,從而
4.1 立即停止執行當前函數
4.2 它defer函數被執行
4.3 返回給它的調用者(caller)
- ...(遞迴重複上述步驟,直到最上層函數)
應用程式停止。
- panic的行為
簡單的總結panic()就有點類似java語言的exception的處理,因而panic的行為和java的exception處理行為就非常類似,行為結合catch,和final語句塊的處理流程。
下面給幾個例子:
例子1:log.Fatal
package mainimport ( "log")func foo() { defer func () { log.Print("3333")} () log.Fatal("4444")}func main() { log.Print("1111") defer func () { log.Print("2222")} () foo() log.Print("9999")}
運行結果:
$ go build && ./main2018/08/20 17:48:44 11112018/08/20 17:48:44 4444
可見defer函數的內容並沒有被執行,程式在log.Fatal(...)處直接就退出了。
例子2:panic()函數
package mainimport ( "log")func foo() { defer func () { log.Print("3333")} () panic("4444")}func main() { log.Print("1111") defer func () { log.Print("2222")} () foo() log.Print("9999")}
運行結果:
$ go build && ./main2018/08/20 17:49:28 11112018/08/20 17:49:28 33332018/08/20 17:49:28 2222panic: 4444goroutine 1 [running]:main.foo() /home/.../main.go:9 +0x55main.main() /home/.../main.go:15 +0x82
可見所有的defer都被調用到了,函數根據父子調用關係所有的defer都被調用直到最上層。
當然如果其中某一層函數定義了recover()功能,那麼panic會在那一層函數裡面被截獲,然後由recover()定義如何處理這個panic,是丟棄,還是向上再拋出。(是不是和exception的處理機制一模一樣呢?)