golang defer解析
定义
defer 是 Go 语言提供的一种用于注册延迟调用的机制:让函数或语句可以在当前函数执行完毕后执行。
- 每次 defer 语句执行的时候,会把函数“压栈”,函数参数会被拷贝下来。
 
- 当外层函数退出时,多个defer后进先出(LIFO)的顺序执行,即最后一个defer语句会最先执行。)。
 
- 如果 defer 执行的函数为 nil, 那么会在最终调用函数的产生 panic。
 
不能在defer里面返回值
什么情况下会调用 defer 延迟过的函数呢?
- 当函数执行了 
return 语句后 
- 当函数处于 
panicing 状态,也就是程序中 panic 回溯上来到当前函数范围内时 
进阶
defer return拆解
- defer在return之前执行 但return xxx这一条语句并不是一条原子指令
 
- 函数返回的过程是这样的:先给返回值赋值,然后调用defer表达式,最后才是返回到调用函数中。
 
1
2
3
4
5
6
7
  | 
// res=0;defer res++;return res  
func defer1() (res int) {  
    defer func() {  
       res++  
    }()  
    return 0  
}
  | 
 
1
2
3
4
5
6
7
8
  | 
// res=1;defer res++;return res  
func defer2() (res int) {  
    res = 1  
    defer func() {  
       res++  
    }()  
    return res  
}
  | 
 
1
2
3
4
5
6
7
8
  | 
// x=1;res++;return x  
func defer3() int {  
    res := 1  
    defer func() {  
       res++  
    }()  
    return res  
}
  | 
 
1
2
3
4
5
6
7
8
  | 
// r=res;defer res++;return r  
func defer4() (r int) {  
    res := 1  
    defer func() {  
       res++  
    }()  
    return res  
}
  | 
 
1
2
3
4
5
6
7
  | 
// res=0;new_res++;return res  
func defer5() (res int) { //命名返回值在函数开始时会被初始化为其类型的零值,而匿名返回值则不会
    defer func(res int) { //这里的res是传值 不会更改外部的res  
       res++  
    }(res)  
    return res  
}
  | 
 
1
2
3
4
5
6
7
  | 
// r=0;r++;return r  
func defer6() (r int) {  
    defer func(res *int) { //这里的res是传引用 会更改外部的res  
       *res++  
    }(&r)  
    return r  
}
  | 
 
defer 函数对外部变量的引用
在 defer 函数定义时,对外部变量的引用是有两种方式的,分别是作为函数参数和作为闭包引用。
- 作为函数参数,则在 defer 定义时就把值传递给 defer,并被 cache 起来。
 
- 作为闭包引用的话,则会在defer 函数真正调用时根据整个上下文确定当前的值。
 
defer 后面的语句在执行的时候,函数调用的参数会被保存起来,也就是复制了一份。真正执行的时候,实际上用到的是这个复制的变量,因此如果此变量是一个“值”,那么就和定义的时候是一致的。如果此变量是一个“引用”,那么就可能和定义的时候不一致。
defer 后面跟的是闭包,必然是引用类型的变量。
1
2
3
4
5
6
  | 
func main() {
	startedAt := time.Now()
	defer fmt.Println(time.Since(startedAt))//并不能返回1
	
	time.Sleep(time.Second)
}
  | 
 
1
2
3
4
5
6
  | 
func main() {
	startedAt := time.Now()
	defer func() { fmt.Println(time.Since(startedAt)) }()//返回1
	
	time.Sleep(time.Second)
}
  | 
 
References