golang的log.Fatal()和panic()函數(shù)的區(qū)別
在講兩者區(qū)別之前我們先看一下os.Exit()函數(shù)的定義:
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.
注意兩點(diǎn):
- 應(yīng)用程序馬上退出。
- defer函數(shù)不會(huì)執(zhí)行。
再來看log.Fatal函數(shù)定義
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)
}
總結(jié)起來log.Fatal函數(shù)完成:
- 打印輸出內(nèi)容
- 退出應(yīng)用程序
- defer函數(shù)不會(huì)執(zhí)行
和os.Exit()相比多了第一步。
再來看內(nèi)置函數(shù)panic()函數(shù)定義:
// 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{})
注意幾點(diǎn):
- 函數(shù)立刻停止執(zhí)行 (注意是函數(shù)本身,不是應(yīng)用程序停止)
- defer函數(shù)被執(zhí)行
- 返回給調(diào)用者(caller)
- 調(diào)用者函數(shù)假裝也收到了一個(gè)panic函數(shù),從而
4.1 立即停止執(zhí)行當(dāng)前函數(shù)
4.2 它defer函數(shù)被執(zhí)行
4.3 返回給它的調(diào)用者(caller) - ...(遞歸重復(fù)上述步驟,直到最上層函數(shù))
應(yīng)用程序停止。 - panic的行為
簡(jiǎn)單的總結(jié)panic()就有點(diǎn)類似java語言的exception的處理,因而panic的行為和java的exception處理行為就非常類似,行為結(jié)合catch,和final語句塊的處理流程。
下面給幾個(gè)例子:
例子1:log.Fatal
package main
import (
"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")
}
運(yùn)行結(jié)果:
$ go build && ./main
2018/08/20 17:48:44 1111
2018/08/20 17:48:44 4444
可見defer函數(shù)的內(nèi)容并沒有被執(zhí)行,程序在log.Fatal(...)處直接就退出了。
例子2:panic()函數(shù)
package main
import (
"log"
)
func foo() {
defer func () { log.Print("3333")} ()
panic("4444")
}
func main() {
log.Print("1111")
defer func () { log.Print("2222")} ()
foo()
log.Print("9999")
}
運(yùn)行結(jié)果:
$ go build && ./main
2018/08/20 17:49:28 1111
2018/08/20 17:49:28 3333
2018/08/20 17:49:28 2222
panic: 4444
goroutine 1 [running]:
main.foo()
/home/.../main.go:9 +0x55
main.main()
/home/.../main.go:15 +0x82
可見所有的defer都被調(diào)用到了,函數(shù)根據(jù)父子調(diào)用關(guān)系所有的defer都被調(diào)用直到最上層。
當(dāng)然如果其中某一層函數(shù)定義了recover()功能,那么panic會(huì)在那一層函數(shù)里面被截獲,然后由recover()定義如何處理這個(gè)panic,是丟棄,還是向上再拋出。(是不是和exception的處理機(jī)制一模一樣呢?)