Golang的panic和recover非常影响效率,建议少用。panic类似c++的throw,可以抛出一个异常,recover经常用在defer中去捕获一个panic,使这个panic不再往上传递,这样程序可以继续往下运行。
为了更深入了解,例子如下:
1 package main
2
3 import (
4 "fmt"
5 )
6
7 func main() {
8 fmt.Println("Hello, i am main begin..")
9 defer func() {
10 fmt.Printf("I am main-defer-1\n")
11 }()
12 defer func() {
13 fmt.Printf("I am main-defer-2\n")
14 }()
15
16 foo()
17
18 fmt.Println("Hello, i am main end..")
19
20 defer func() { fmt.Printf("I am main-defer-3\n"); } ()
21 }
func foo() {
24 fmt.Println("Hello, i am foo begin..")
25 defer func() {
26 fmt.Println("I am foo-defer-1")
27
28 if r := recover(); r != nil {//这里捕获了panic,foo的调用者会以为foo是正常退出,foo后面的语句会运行
29 fmt.Println("recover in foo,", r)
30 }
31
32 }()
33 defer func() { fmt.Println("I am foo-defer-2"); } ()
34 i := 0
35 fmt.Printf("3/0 = %d\n", 3/i)<<<<模拟panic
36 }
运行结果:
root@ubuntu:recoverPanic# go run Recover.go
Hello, i am main begin..
Hello, i am foo begin..
I am foo-defer-2
I am foo-defer-1
recover in foo, runtime error: integer divide by zero
Hello, i am main end..
I am main-defer-3
I am main-defer-2
I am main-defer-1
root@ubuntu:recoverPanic#
我们看看不用recover捕获panic的结果:
1 package main
2
3 import (
4 "fmt"
5 )
6
7 func main() {
8 fmt.Println("Hello, i am main begin..")
9 defer func() {
10 fmt.Printf("I am main-defer-1\n")
11 }()
12 defer func() {
13 fmt.Printf("I am main-defer-2\n")
14 }()
15
16 foo()
17
18 fmt.Println("Hello, i am main end..")
19
20 defer func() { fmt.Printf("I am main-defer-3\n"); } ()
21 }
22
23 func foo() {
24 fmt.Println("Hello, i am foo begin..")
25 defer func() {
26 fmt.Println("I am foo-defer-1")
27 /*<<<<<<<<注释掉recover,panic会一直往上传递,foo后面的语句没有运行。
28 if r := recover(); r != nil {
29 fmt.Println("recover in foo,", r)
30 }
31 */
32 }()
33 defer func() { fmt.Println("I am foo-defer-2"); } ()
34 //i := 0
35 //fmt.Printf("3/0 = %d\n", 3/i)
36 panic(3) <<<这里改为panic调用。
37
38 }
root@ubuntu:recoverPanic# go run Recover.go
Hello, i am main begin..
Hello, i am foo begin..
I am foo-defer-2
I am foo-defer-1
<<<<foo 后面的语句没有运行,但是foo前面注册的defer会先运行,才退出main。
I am main-defer-2I am main-defer-1
panic: 3
goroutine 1 [running]:<<<下面的输出有点像gdb的back trace
main.foo()
/root/my_go/recoverPanic/Recover.go:36 +0x109
main.main()
/root/my_go/recoverPanic/Recover.go:16 +0xdb
exit status 2
root@ubuntu:recoverPanic#
==================================runtime也可以输出堆栈信息:
1 package main
2
3 import (
4 "fmt"
5 "runtime"
6 "os"
7 )
8
9 func main() {
10 fmt.Println("Hello, i am main begin..")
11 defer func() { <<<<<这里用runtime的Stack输出堆栈信息,可以用在recover捕获panic中用来打印堆栈。
12 fmt.Printf("I am main-defer-1\n")
13 var buf [4096]byte
14 n := runtime.Stack(buf[:], false)
15 os.Stdout.Write(buf[:n])
16 }()
17 defer func() {
18 fmt.Printf("I am main-defer-2\n")
19 }()
20
21 foo()
22
23 fmt.Println("Hello, i am main end..")
24
25 defer func() { fmt.Printf("I am main-defer-3\n"); } ()
26 }
27
28 func foo() {
29 fmt.Println("Hello, i am foo begin..")
30 defer func() {
31 fmt.Println("I am foo-defer-1")
32 /*
33 if r := recover(); r != nil {
34 fmt.Println("recover in foo,", r)
35 }
36 */
37 }()
38 defer func() { fmt.Println("I am foo-defer-2"); } ()
39 //i := 0
40 //fmt.Printf("3/0 = %d\n", 3/i)
41 panic(3)
42
43 }
root@ubuntu:recoverPanic# go run Recover.go
Hello, i am main begin..
Hello, i am foo begin..
I am foo-defer-2
I am foo-defer-1
I am main-defer-2
I am main-defer-1
goroutine 1 [running]:<<<runtime 输出的比程序崩溃自己输出的多两行:
main.main.func1()
/root/my_go/recoverPanic/Recover.go:14 +0x8f
panic(0x48c840, 0xc42000e218)
/usr/lib/go-1.8/src/runtime/panic.go:489 +0x2cf
main.foo()
/root/my_go/recoverPanic/Recover.go:41 +0x109
main.main()
/root/my_go/recoverPanic/Recover.go:21 +0xdb
panic: 3
goroutine 1 [running]:<<<<<程序崩溃自己的输出的还是会输出,不会英文调用runtime而不输出。
main.foo()
/root/my_go/recoverPanic/Recover.go:41 +0x109
main.main()
/root/my_go/recoverPanic/Recover.go:21 +0xdb
exit status 2
root@ubuntu:recoverPanic#