Go panic+defer+recover理解加使用
业务逻辑中,Golang通过返回error捕获错误,但当遇到一些触发程序的异常时,会导致程序崩溃,这时就是需要recover这种捕获异常方式了,recover通常与defer同时出现
Defer
defer语句函数放入栈中,执行defer的顺序满足先进后出原则,严格按照这个顺序,不会因为return等操作就不执行
举一个简单的recover和defer配合使用的例子
import ( "fmt" "time" ) func main() { test() for { fmt.Println("main()下面的代码...") time.Sleep(time.Second) } } func test() { defer func() { err := recover() // recover()内置函数,可以捕获到异常 if err != nil { // 捕获到异常 fmt.Println("err=", err) } }() num1 := 10 num2 := 0 res := num1 / num2 fmt.Println("res=", res) }
打印:
err= runtime error: integer divide by zero
main()下面的代码...
main()下面的代码...
使用defer函数的一些坑
1.不要在循环中使用defer,defer函数会入栈,可能会因为爆栈而导致程序崩溃,解决办法(1.在循环之外定义defer,2.将循环体包成方法体,在方法体中调用defer)
2.调用defer时参数问题,执行到defer函数时,就会进行参数复制(记住这个复制的时刻,并不是等最后执行的时候才是复制),此时复制是值复制(等同于普通函数的入参)
3.defer
语句延迟执行了一个匿名函数,因为这个匿名函数捕获了外部函数的局部变量v
,这种函数我们一般叫闭包。闭包对捕获的外部变量并不是传值方式访问,而是以引用的方式访问
func Inc() (v int) { defer func(){ v++ } () return 42 }
print(Inc)#43
another case
func main() { for i := 0; i < 5; i++ { defer func() { fmt.Println(i) }() } }
记录的是引用,最后加完的i就是5 5 5 5 5 5
4.跨协程失效,panic只会触发当前协程的defer函数
func main() { defer println("in main") go func() { defer println("in goroutine") panic("") }() time.Sleep(1 * time.Second) }
in goroutine
panic :
1秒钟后输出 in main
5.失效的崩溃恢复,recover不在defer里面,所以不会被捕获到
func main() { defer fmt.Println("in main") if err := recover(); err != nil { fmt.Println(err) } panic("unknown err") }
6.嵌套崩溃
func main() { defer fmt.Println("in main") defer func() { defer func() { panic("panic again and again") }() panic("panic again") }() panic("panic once") }
in main
panic: panic once
panic: panic again
panic: panic again and again
goroutine 1 [running]:
7.如果其他协程发生panic,不捕获的话会自己导致主线程和其他协程执行失败,并且在主线程无法捕获panic
8.方法里有协程,协程对某一些变量的修改会被defer中捕获到吗?得看goroutine和主方法哪个执行快,defer是随着主方法的
本文来自博客园,作者:LeeJuly,转载请注明原文链接:https://www.cnblogs.com/peterleee/p/13448587.html