KN001.defer,panic和recover

1.关键字说明

1.1 defer

  • defer是go提供的一种资源处理的方式
  • 处理 类似文件关闭、锁释放,连接关闭等等,类似于java的finally的方法,在方法体执行结束后执行的
  • 执行 先进后出 的,与 return 搭配时,return 优先级高于 defer

1.2 panic和recover

  • panic 和 recover 是 Go 收集程序发生异常的 机制
  • 通俗理解的表达
    • panic 前 必须定义 defer (换句话说,defer必须定义在panic之前)
    • recover 必须定义在defer 内部,否负责无效
    • panic 发生异常,如果没有 defer中的recover 接收处理,导致程序中断
  • 优雅的表达
    • defer 需要放在 panic 之前定义,另外 recover 只有在 defer 调用的函数中才有效
    • recover 处理异常后,逻辑并不会恢复到panic那个点去,函数跑到 defer 之后的那个点

2.示例说明规则

2.1 基础热身

示例1

func defer_call() {
    defer func() { fmt.Println("打印前") }()
    defer func() { fmt.Println("打印中") }()
    defer func() { fmt.Println("打印后") }()

    panic("触发异常")
    // 执行结果
    //  打印后
    // 打印中
    // 打印前
    // panic: 触发异常
}

示例2

func defer_call1() {
    defer func() { fmt.Println("打印前") }()
    defer func() { fmt.Println("打印中") }()
    defer func() { fmt.Println("打印后") }()

    panic("触发异常")

    fmt.Println("panic 后面的代码段...")

    // 执行结果
    //  打印后
    // 打印中
    // 打印前
    // panic: 触发异常
}
// 扩展 示例 ,验证 发生了 panic 后, 后面的 defer 代码亦将无法执行
func defer_call2() {
    defer func() { fmt.Println("打印前") }()
    defer func() { fmt.Println("打印中") }()
    defer func() { fmt.Println("打印后") }()

    panic("触发异常")

    defer fmt.Println("panic 后面的代码段...")

    // 执行结果
    //  打印后
    // 打印中
    // 打印前
    // panic: 触发异常
}

注意
panic发生异常,如果没有recover 捕获 ,程序将中断

示例3


// defer 的用法的三个原则
// 1. 在defer表达式被运算的同时,defer函数的参数也会被运算
// 2. defer函数在一个函数return之后遵循后进先出的调用原则
// 3. defer函数可能读取并赋值给所在函数的返回值
func deferRule1() {
    i := 0
    defer fmt.Println(i)
    i++
    return
    // 输出结果是
    // 0
}

func deferRule2() {
    for i := 0; i < 4; i++ {
        defer fmt.Print(i)
    }
    // 输出结果是
    // 3210
}

func deferRule3() (i int) {
    defer func() {
        i++
    }()
    return 1

    // 执行结果
    // i = 2
}

// 针对defer的第三点,存在3种情况
func deferRule3_1() (result int) {

    defer func() {
        result++ // result = result + 1 , result = 1
    }()

    return 0 // result = 0
    // 执行结果
    // 1

    // 详细解说
    /*
        func deferRule3_1() (result int) {
             result = 0  //return语句不是一条原子调用,return xxx其实是赋值+ret指令
             func() {    //defer被插入到return之前执行,也就是赋返回值和ret指令之间
                 result++
             }()
             return
        }
    */
}

func deferRule3_2() (r int) {
    t := 5
    defer func() {
        t = t + 5 // t = t + 5 , t = 10
    }()

    return t // t = 5 , r = t , r = 5
    //执行结果
    //5

    // 详细解说
    /*
        func deferRule3_2() (r int) {
             t := 5
             r = t     //赋值指令
             func() {  //defer被插入到赋值与返回之间执行,这个例子中返回值r没被修改过
                 t = t + 5
             }
             return    //空的return指令
        }
    */
}

func deferRule3_3() (r int) {
    defer func(r int) {
        r = r + 5 //r = r + 5 , r = 5
    }(r) // r = 0 , r 值传递到defer 函数
    return 1 // r = 1

    // 执行解雇
    // 1

    // 详细解说
    /*
        func deferRule3_3() (r int) {
             r = 1         //给返回值赋值
             func(r int) { //这里改的r是传值传进去的r,不会改变要返回的那个r值
                  r = r + 5
             }(r)
             return        //空的return
        }
    */
}

示例4

// panic 和 recover的
func f() {
    defer func() {
        fmt.Println("defer start")
        if err := recover(); err != nil {
            fmt.Println(err) // 这里的err其实就是panic传入的内容,“bug”
        }
        fmt.Println("defer end")
    }()

    for {
        fmt.Println("func begin")
        a := []string{"a", "b"}
        fmt.Println(a[3])       // 越界访问,肯定出现异常
        panic("bug")            // 上面已经出现异常了,所以肯定走不到这里了。
        fmt.Println("func end") // 不会运行的.
        time.Sleep(1 * time.Second)
    }
    //执行结果
    // func begin
    // defer start
    // runtime error: index out of range [3] with length 2
    // defer end
}

panic 和 recover延迟

// panic 和 recover的, 发生异常后,执行 recover 的 defer,然后在执行 defer栈的函数
func f1() {
    defer fmt.Println("defer recover before ...")
    defer func() {
        fmt.Println("defer start")
        if err := recover(); err != nil {
            fmt.Println(err) // 这里的err其实就是panic传入的内容,“bug”
        }
        fmt.Println("defer end")
    }()

    defer fmt.Println("defer recover after ...")
    for {
        fmt.Println("func begin")
        a := []string{"a", "b"}
        fmt.Println(a[3])       // 越界访问,肯定出现异常
        panic("bug")            // 上面已经出现异常了,所以肯定走不到这里了。
        fmt.Println("func end") // 不会运行的.
        // time.Sleep(1 * time.Second)
    }
    fmt.Println("main end")
    //执行结果
    // func begin
    // defer recover after ...
    // defer start
    // runtime error: index out of range [3] with length 2
    // defer end
    // defer recover before ...
}

// 如果存在多个recover的defer栈时,按照 defer栈的原则,先进后出原则,第一个 出栈的 defer 捕获 panic的异常后,后面的 recover的defer 不会再捕获到panic的异常
func f2() {
    defer fmt.Println("defer recover before ...")
    defer func() {
        fmt.Println("defer start")
        if err := recover(); err != nil {
            fmt.Println("defer recover 1 ... ", err) 
        }
        fmt.Println("defer end")
    }()

    defer func() {
        if err := recover(); err != nil {
            fmt.Println("defer recover 2 ...", err)
        }
        fmt.Println("defer recover after ...")
    }()

    for {
        fmt.Println("func begin")
        a := []string{"a", "b"}
        fmt.Println(a[3])       // 越界访问,肯定出现异常
        panic("bug")            // 上面已经出现异常了,所以肯定走不到这里了。
        fmt.Println("func end") // 不会运行的.
        // time.Sleep(1 * time.Second)
    }
    fmt.Println("main end")
    // func begin
    // defer recover 2 ... runtime error: index out of range [3] with length 2  // 异常被 recover2 捕获
    // defer recover after ...
    // defer start
    // defer end
    // defer recover before ...
}

3.总结

  • defer的执行顺序
    • 多个defer的执行顺序,原则是 先进后出
    • return执行 的优先级 高于 defer
  • panic 和 recover
    • defer 必须定义在 panic之前,recover 必须定义在defer内才有效
    • recover 处理异常后,逻辑并不会恢复到panic那个点去,函数跑到 defer 之后的那个点
    • 多个defer recover栈,首个出栈的defer recover 捕获异常,其他的 recover的defer 将捕获不到异常。
posted @ 2021-10-14 10:27  可可逗豆  阅读(115)  评论(0)    收藏  举报