深入理解Golang中的defer关键字

对于defer的简单用法,可以参考之前的随笔!!!

本次将详细介绍defer,以及其底层实现,具体的应用场景!!!

Go语言的defer会在当前函数或者方法返回之前执行传入的函数。它会经常被用于关闭文件,关闭数据库连接以及解锁资源。

这里提出两个问题:

  1.defer关键字调用时机以及多次调用defer时的知心顺序是如何确定的

  2.defer关键字使用传值的方式传递参数时会进行预计算,导致不符合预期的结果

作用域

  向defer关键字传入的函数会在函数返回之前运行。这里看两个例子

package main

import "fmt"

func main() {
	{
		defer println("defer ends")
        fmt.Println("block ends")
	}
	fmt.Println("main ends")
}
输出结果
block ends
main ends
defer ends

import "fmt"

func main() {
	{
		defer println("defer ends")
        fmt.Println("block ends")
	}
	fmt.Println("main ends")
}
输出结果:
4
3
2
1
0

  defer传入的函数不是在提出代码块的作用域执行,只会在当前函数或者方法返回之前被调用。

数据结构(摘自go语言设计与实现)

部分参数说明:

  siz:参数和结果的内存大小

  sp和pc:分别代表栈指针和调用方法的程序计数器

  fn:defer关键字传入的函数

  _panic:触发延迟调用的结构体,可能为空

运行过程

defer关键字的运行时实现分成两个部分:

  runtime.deferproc:函数负责新的延迟调用

  runtime.deferreturn:函数负责在函数调用结束时执行所有的延迟调用

runtime.deferproc会为defer创建一个新的runtime._defer结构体,设置它的函数指针fn,程序计数器sp和栈指针pc并将相关参数拷贝到相邻的内存空间中。

runtime.deferreturn 会从 Goroutine _defer 链表中取出最前面的 runtime.jmpdefer 函数传入需要执行的函数和参数

来看一下《go语言设计与实现》给的关于defer的总结:

 

posted @ 2023-05-11 20:20  99号的格调  阅读(64)  评论(0)    收藏  举报