• 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

  • 相关阅读:
    小程序之滚动选择器(时间、普通、日期)
    bzoj 4825: [Hnoi2017]单旋 LCT
    bzoj 4821: [Sdoi2017]相关分析 线段树
    bzoj 4766: 文艺计算姬 矩阵树定理
    bzoj 4031: 小Z的房间 矩阵树定理
    bzoj 4822~4824 CQOI2017题解
    bzoj 4817: [Sdoi2017]树点涂色 LCT+树链剖分+线段树
    bzoj 4816: [Sdoi2017]数字表格
    bzoj 4537: [Hnoi2016]最小公倍数 分块+并查集
    bzoj 4653: [Noi2016]区间
  • 原文地址:https://www.cnblogs.com/peterleee/p/13448587.html
Copyright © 2020-2023  润新知