3、go defer 应用场景及原理

0 阅读1分钟
> 路径:

go/src/runtime/panic.go runtime/runtime2.go (结构))

应用层面

一、作用:延迟调用

尽管defer底层使用链表实现,但从使用和行为层面,它模拟了栈的后进先出特性。(后续会讲到)

二、特点:延迟调用,多个defer函数的调用顺序为后进先出

三、应用场景:资源的释放、异常捕获和处理

1、资源释放(请求、数据库连接、文件访问、锁释放、记录执行时间、事物回滚...)下面只列举一个。

func TestDefer(t *testing.T) {
    client := http.Client{}
    resp, err := client.Get("https://juejin.cn/")
    if err != nil {
       fmt.Println(err.Error())
    }
    defer resp.Body.Close()
}

2、异常捕获

func TestDefer(t *testing.T) {
    defer func() {
       err := recover()
       if err != nil {
          fmt.Println(err)
       }
    }()
    panic("test panic")
}

原理 - 后进先出

一个Goroutine 对应一个defer链表

1、结构

image.png

image.png

2、defer 如何放到链表当中的 只要遇到defer 关键词就会调用 deferproc 函数

image.png 创建一个defer结构:newdefer()

d := newdefer()
d.link = gp._defer
gp._defer = d

这里 newdefer() 创建了一个新的 defer 记录 d。

d.link = gp._defer 将新节点 d 的 link 指针指向当前链表的头部(gp._defer),

然后 gp._defer = d 把新节点 d 设置为链表的新头部。

这个过程非常高效,无论链表当前有多长,都只需要固定的几步操作就可以完成新节点的插入。