对defer的认识

对defer的认识,第1张

defer defer内部结构

源码包src/runtime/panic.go定义了两个方法分别用于创建defer和执行defer。

deferproc(): 在声明defer处调用,其将defer函数存入goroutine的链表中;deferreturn():在return指令,准确的讲是在ret指令前调用,其将defer从goroutine链表中取出并执行。

可以简单这么理解,在编译阶段,声明defer处插入了函数deferproc(),在函数return前插入了函数deferreturn()。

defer延迟的原因
func A(){
    defer B()
}

//内部流程
func A(){
    //1.注册
    r = deferproc(8,B)
    //2.执行
    runtime.deferreturn()
    return
}

这正是因为先注册后执行才造成延迟的原因

先defer的后执行的原因

defer信息会注册到一个链表中,而当前执行的goroutine持有这个链表的头指针 ,每个goroutine运行时都有一个对应的结构体g,其中有一个字段,指向defer链表头,defer链表链接起来的是一个一个_defer结构体,新添加的defer会添加到链表头,执行时也是从头开始

defer,return和返回值的的执行顺序 return包含两个步骤: 第一步是给返回值赋值,(若为有名返回值则直接赋值,若为匿名返回值则先声明再赋值)第二步是调用RET返回指令并传入返回值,RET会检测defer是否存在,若存在就先执行defer语句,最后RET携带返回值退出函数

‍‍defer、return、返回值三者的执行顺序应该是:return最先给返回值赋值;接着defer开始执行一些收尾工作;最后RET指令携带返回值退出函数。

下面案例验证上面结论

func f1() int {
	x := 5
	defer func() {
		x++
	}()
	return x//5
}
解释:没有返回值名字,golang会给你一个返回值起一个名字假如叫zxp return时把x的值给了zxp,返回值返回之前先执行defer执行完后返回值返回,x的值增加了,
但是返回的是zxp的值5



func f2() (x int) {
	defer func() {
		x++
	}()
	return 5//6
}
解释:这里返回值有名字为x,return时x = 5;return x,返回值返回时遇到defer,因此x++,此时x为6,所以返回值为6



func f3() (y int) {
	x := 5
	defer func() {
		x++
	}()
	return x//5
}
解释:返回值名字为y,return时相当于y = x = 5 return y,然后defer ,x增加但与y无关,所以是5




func f4() (x int) {
	defer func(x int) {
		x++
	}(x)
	return 5//5
}
解释:自执行函数中的x相当于一个拷贝(golang是值传递),地址变化,返回的是原地址的x的值,所以是5


另外一种写法
func calc(index string, a, b int) int {
   ...
}
func main{
	defer calc("AA", x, calc("A", x, y))
	defer calc("BB", x, calc("B", x, y))
}
 解释:defer里面的参数是要在入栈之前需要提前确定好的,首先要执行一下calc("A", x, y),因为它是外层calc函数的参数

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/993918.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存