概念
panic
能够改变程序的控制流,调用panic
后会立刻停止执行当前函数的剩余代码,并在当前 Goroutine 中递归执行调用方的defer
;recover
可以中止panic
造成的程序崩溃。它是一个只能在defer
中发挥作用的函数,在其他作用域中调用不会发挥作用;
现象
-
panic
只会触发当前 Goroutine 的defer
package main import ( "fmt" "time" ) func main() { defer fmt.Println("主函数defer执行...") go func() { defer fmt.Println("子函数defer执行...") panic("子函数报错...") }() time.Sleep(time.Second) fmt.Println("正常业务代码...") }
go run main.go 子函数defer执行... panic: 子函数报错... goroutine 18 [running]: main.main.func1() /Users/user/go/src/go-demo/defer/main.go:13 +0x73 created by main.main /Users/user/go/src/go-demo/defer/main.go:11 +0x79 exit status 2
-
recover
只有在defer
中调用才会生效package main import ( "fmt" ) func main() { defer fmt.Println("主函数defer执行...") if err := recover(); err != nil { fmt.Println("进入recover...") } panic("主函数报错...") }
go run main.go 主函数defer执行... panic: 主函数报错... goroutine 1 [running]: main.main() /Users/user/go/src/go-demo/defer/main.go:14 +0x8d exit status 2
上面的 recover 并不会生效,因为 recover 只有在 panic 之后才会调用,上面的代码会先执行 recover,所以不会生效。如果想使 recover 生效,就必须放到 defer 中。
我们修改下上面的代码:
package main import ( "fmt" ) func main() { defer func() { fmt.Println("主函数defer执行...") if err := recover(); err != nil { fmt.Println("进入recover...") fmt.Println(err) } }() panic("主函数报错...") }
go run main.go 主函数defer执行... 进入recover... 主函数报错...
-
panic
允许在defer
中嵌套多次调用package main func main() { defer func() { defer func() { panic("defer defer panic") }() panic("defer panic") }() panic("主函数报错...") }
go run main.go panic: 主函数报错... panic: defer panic panic: defer defer panic goroutine 1 [running]: main.main.func1.1() /Users/user/go/src/go-demo/defer/main.go:6 +0x27 panic({0x1059120, 0x1075b38}) /usr/local/go/src/runtime/panic.go:1038 +0x215 main.main.func1() /Users/user/go/src/go-demo/defer/main.go:8 +0x49 panic({0x1059120, 0x1075b18}) /usr/local/go/src/runtime/panic.go:1038 +0x215 main.main() /Users/user/go/src/go-demo/defer/main.go:11 +0x49 exit status 2
讨论区
登录评论