0%

Golang-Defer 详解

defer 触发时机

A “defer” statement invokes a function whose execution is deferred to the moment the surrounding
function returns, either because the surrounding function executed a return statement, reached the
end of its function body, or because the corresponding goroutine is panicking.

Go 官方文档中对 defer 的执行时机做了阐述,分别是。

  • 包裹 defer 的函数返回时
  • 包裹 defer 的函数执行到末尾时
  • 所在的 goroutine 发生 panic 时

defer 执行顺序

当一个方法中有多个 defer 时, defer 会将要延迟执行的方法 “压栈”,当 defer 被触发时,将所有 “压栈” 的方法 “出栈” 并执行。所以 defer 的执行顺序是 LIFO 的。

执行顺序如下

1
2
3
4
5
6
7
# 常规执行

语句1 -> 语句2 -> 语句3 -> 语句4

# 在语句2,语句3中添加defer后执行顺序如下

语句1 -> 语句4 -> 语句3(带defer) -> 语句2(带defer)

defer 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import "fmt"

func main() {
d()
}

func d() {
fmt.Print("start" + " ")
fmt.Print("processing1" + " ")
fmt.Print("processing2" + " ")
fmt.Print("end" + " ")
}
  • 没有 defer:start -> processing1 -> processing2 -> end
  • processing1、processing2 加入 defer:start -> end -> processing2 -> processing1

defer 使用规则

defer 会实时解析参数

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func main() {
i := 0
defer fmt.Println(i)
i ++
return
}
// 0

这是因为虽然我们在 defer 后面定义的是一个带变量的函数: fmt.Println (i). 但这个变量 (i)
在 defer 被声明的时候,就已经确定其确定的值了

defer 的类栈执行

栈:先入后出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import "fmt"

func f1() {
fmt.Println(1)
}

func f2() {
fmt.Println(2)
}
func main() {
defer f1()
f2()
}

// 2\1

defer 可以读取有名返回值

1
2
3
4
5
6
7
8
9
func c() (i int) {
defer func() { i++ }()
return 1
}

func main() {
fmt.Println(c())
}
// 2

在开头的时候,我们说过 defer 是在 return 调用之后才执行的。

这里需要明确的是 defer 代码块的作用域仍然在函数之内,结合上面的函数也就是说,defer 的作用域仍然在 c 函数之内。因此 defer 仍然可以读取 c 函数内的变量 (

如果无法读取函数内变量,那又如何进行变量清除呢…)。
当执行 return 1 之后,i 的值就是 1. 此时此刻,defer 代码块开始执行,对 i 进行自增操作。 因此输出 2.

Powered By Valine
v1.5.2