源码包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最先给返回值赋值;接着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函数的参数
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)