Go語言的defer, panic和recover
本文源自ofollow,noindex" target="_blank">https://blog.golang.org/defer-panic-and-recover
defer
defer語句在Go的函式中屬於一個棧的形式,即在函式執行完成時,根據該語句出現的順序 後進先出進行呼叫。從流程上來講,類似於如下python程式碼:
def defer(func): def wrapper(*args, **kwargs): defers = [] kwargs['defers'] = defers func(*args, **kwargs) for f in reversed(defers): f() return wrapper @defer def foo(defers=None): defers.append(lambda: print("1st defered function in foo")) print("hello") defers.append(lambda: print("2st defered function in foo")) print("world") defers.append(lambda: print("3st defered function in foo")) if __name__ == "__main__": foo()
執行結果:
root@arch tests: python defer.py hello world 3st defered function in foo 2st defered function in foo 1st defered function in foo
但是實際上Go中的defer和上面的程式碼有很大不同。Go的defer有三個特點:
- 被defer的函式的引數值在defer產生的那一行程式碼處就確定了
- defer的函式的執行順序為後進先出----最後derfer的函式最先執行,它們都在函式 返回了值之後開始
- defer的函式可以修改函式返回值
我們來看一個例子:
package main import "fmt" func test_defer() { var i int = 1 defer fmt.Println("1st in test_defer, i = ", i) fmt.Println("hello") i += 1 defer fmt.Println("1st in test_defer, i = ", i) fmt.Println("world") i += 1 defer fmt.Println("1st in test_defer, i = ", i) } func main() { test_defer() }
執行結果:
root@arch tests: go run defer.go hello world 1st in test_defer, i =3 1st in test_defer, i =2 1st in test_defer, i =1
由此可證第一第二點。
接下來我們再看一個例子:
package main import "fmt" func test_defer() (i int){ defer func () { fmt.Println(i) i++ fmt.Println(i) } () return 1 } func main() { i := test_defer() fmt.Println(i) }
執行結果:
root@arch tests: go run defer.go 1 2 2
由此可以看出,函式的執行過程是,執行函式體,然後到return語句,後進先出執行defer 語句,最後返回函式值。
panic, recover
他們倆都是內建函式,和Python中的raise和try...except...
類似。值得注意的是,
即便函式在執行過程中發生了panic,也會執行完被defer的函式。
package main import "fmt" func caller() { defer func () { fmt.Println("defered anonymous function in caller") } () callee() if r := recover(); r != nil { fmt.Println("recovered from callee") } } func callee() { panic(1111) } func main() { caller() }
執行結果:
root@arch tests: go run panic.go defered anonymous function in caller panic: 1111 goroutine 1 [running]: panic(0x48a0a0, 0xc420056190) /usr/lib/go/src/runtime/panic.go:500 +0x1a1 main.callee() /root/tests/panic.go:19 +0x61 main.caller() /root/tests/panic.go:10 +0x4e main.main() /root/tests/panic.go:24 +0x14 exit status 2
可以看出,在panic那一行,該函式立即退出,整個函式也立即退出,但是利用defer的 特性,我們可以做到異常處理:
package main import "fmt" func caller() { defer func () { fmt.Println("defered anonymous function in caller") if r := recover(); r != nil { fmt.Println("recovered from callee") } } () callee() } func callee() { panic(1111) } func main() { caller() }
執行結果:
root@arch tests: go run panic.go defered anonymous function in caller recovered from callee