> 路径:
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、结构
2、defer 如何放到链表当中的 只要遇到defer 关键词就会调用 deferproc 函数
创建一个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 设置为链表的新头部。
这个过程非常高效,无论链表当前有多长,都只需要固定的几步操作就可以完成新节点的插入。