【GoLand】defer与panic异常处理机制

3,320 阅读2分钟

一、defer、panic、recover之间执行顺序

1.defer和panic的执行顺序

defer是后进先出,协程遇到panic时,遍历本协程的defer链表,并执行defer,如果没有遇到recover,遍历完本协程的defer链表后,向stderr跑出panic信息

package  main
import "fmt"

func main () {
    defer_func()
}

func defer_func() {
    defer func () {
        fmt.Println("1")
    } ()
    defer func () {
        fmt.Println("2")
    } ()
    defer func () {
        fmt.Println("3")
    } ()
    panic("error")
}

2.defer、panic和recover的执行顺序

如果遇到recover,返回recover处继续往下执行,不会抛出panic异常信息

package  main
import "fmt"

func main () {
    defer_func()
}

func defer_func() {
    defer func () {
        if err := recover(); err != nil {
            fmt.Println("recover")
        }
        fmt.Println("1")
    } ()
    defer func () {
        fmt.Println("2")
    } ()
    defer func () {
        fmt.Println("3")
    } ()
    panic("error")
}

3.defer及其入参的执行顺序

这里,有4个函数,他们的index序号分别为1,2,3,4。
那么这4个函数的先后执行顺序是什么呢?
这里面有两个defer, 所以defer一共会压栈两次,先进栈1,后进栈2。 那么在压栈function1的时候,需要连同函数地址、函数形参一同进栈,那么为了得到function1的第二个参数的结果,所以就需要先执行function3将第二个参数算出,那么function3就被第一个执行。同理压栈function2,就需要执行function4算出function2第二个参数的值。然后函数结束,先出栈fuction2、再出栈function1. 所以顺序如下:

  • defer压栈function1,压栈函数地址、形参1、形参2(调用function3) --> 打印3
  • defer压栈function2,压栈函数地址、形参1、形参2(调用function4) --> 打印4
  • defer出栈function2, 调用function2 --> 打印2
  • defer出栈function1, 调用function1--> 打印1
package main

import "fmt"

func function(index int, value int) int {

    fmt.Println(index)

    return index
}

func main() {
    defer function(1, function(3, 0))
    defer function(2, function(4, 0))
}

二、defer源码解读

1.创建defer函数deferproc

获取当前go语言协程,创建新的defer结构体,defer结构体中保存函数地址和函数入参,加入协程的defer链表中(头插法)

2.执行defer函数deferreturn

获取当前协程的defer链表,顺序遍历defer结构体,取出defer函数跳转过去执行,因为deferproc是头插法,所以顺序遍历是逆序执行的

三、参考网站

draveness.me/golang/docs…
studygolang.com/articles/27…