go语言中的defer关键字

在Go语言里面 有一个关键字 我们经常会碰到 就是 defer ,关于defer我们需要记住以下几点:

1.defer语句会在该函数结束的时候被调用,即使后面的语句运行时出现异常了defer语句仍然会被执行。

2.如果defer语句中引用了参数,则该参数的值将是程序到defer这一行的时候的值,而与后面的语句没有关系。

因此,defer通常用来释放函数内部变量。

在看下面的代码:

func test01() {

fmt.Println("我是测试01")

a := 10

defer fmt.Println("a=", a)

a++

}

//未引用参数 跟测试一一样

func test02() {

fmt.Println("我是测试02")

a := 10

defer func() {

fmt.Println("a=", a)

}()

a++

}



//此处引用了参数

func test03() {

fmt.Println("我是测试03")

a := 10

defer func(a int) {

fmt.Println("a=", a)

}(a)

a++

}

解释:

tese01 :代码执行到 defer 那一行时候 因为此时引用了参数,所以 此时的数值 就是当前a的数值 跟后面无关.所以 a=10

test02:正常执行完毕之后 a的数值 就是程序执行完事之后的数值 所以a=11.

test03:因为defer 引用了参数 ,跟test01一个道理

上面的是简单的一个调用,目的是告诉defer 的作用 以及简单说明,那么当我们在同时使用多个defer 时候会出现什么结果呢?会不会按照代码调用顺序来呢?我们继续用代码来看:

func test01() {

fmt.Println("我是测试01")

a := 10

defer fmt.Println("第一次 a=", a)

defer fmt.Println("第二次 a=", a)

a++

}

结果:

("第二次 a=", a) 这个被先输出了,这是为什么呢?

这是因为每一个协程都会维护一个延迟调用堆栈,按照代码顺序把需要延迟调用的函数压入栈中,当函数进入退出阶段后,就会从延迟调用堆栈中取出需要执行的函数调用并执行。按照先进后出的原则来执行.

defer的数据结构:

type _defer struct { 

siz int32 //参数大小 

started bool // defer是否被调用过的标识 

sp uintptr // sp at time of defer pc uintptr 

fn *funcval // defer 后面跟的function _panic *_panic // panic that is running defer 

link *_defer // 链表结构

defer后面一定要接一个函数的,所以defer的数据结构根一般函数类似,也有栈指针、程序计数器、

函数地址等等。

与函数不同的一点是它含有一个指针,可用于指向另一个defer,每个goroutine数据结构中实际上也有一个defer指针,该指针指向一个defer的链表,每次声明一个defer时就将defer插入到单链表表头,每次执行defer就从单链表表头取出一个defer执行。

在deferproc命令的源码中看到这一块代码:

deferproc命令会创建一个_defer类型的数据,它实际上是一个链表类型,有一个字段link指向下一个_defer数据。在当前协程上会保存这个_defer链表的头部,每次创建defer的时候就会插入到协程_defer链表的头部,形成一个基于链表的堆栈。

看看deferreturn命令的源码:

当我们进入退出阶段,执行deferreturn命令的时候,会从当前协程的_defer链表中取出头部,并把下一个元素作为_defer链表的头部,然后再使用jmpdefer指令完成跳转调用,jmpdefer完全是使用汇编完成.

3.defer中使用闭包函数的时候,只有最后一次调用是被延迟执行的

看下面的代码:

func main() {

fmt.Println("a1")

defer test04()()

fmt.Println("a2")



}



func test04() func() {

fmt.Println("我是测试04第一次调用")

b:= func() {

fmt.Println("我是测试04第二次调用")

}

return b

}

错误:

按照我们自己理解的打印结果是:

a1 a2 我是测试04第一次调用,我是测试04第二次调用

正确结果:

解释:当程序运行的时候,首先会打印出 a1,到test04函数 发现这个函数是一个defer修饰的,会进去查看 ,内部又是一个连续调用的,那么就会将最后一次调用延迟,其它的正常输出,所以 就出现上图那样的结果.

这就可以解释:

defer中使用闭包函数的时候,只有最后一次调用是被延迟执行的.

猜你喜欢

转载自blog.csdn.net/FindHuni/article/details/105677277