defer 的三个规则:
- 规则一:defer 声明时,其后面函数参数会被实时解析。
- 规则二:defer 执行顺序为先进后出(FILO)(First Insert Last Out)。
- 规则三:defer 可以读取函数的有名返回值。
defer函数参数会被实时解析#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
package main
import "fmt"
func main() {
var i int = 1
// 规则一:当defer被声明时,其后面函数参数会被实时解析
// 注意,fmt.Println 在defer后面,它的参数会实时计算
// 输出:result => 2 (而不是4)
defer fmt.Println("result1 =>", func() int {return i*2}())
i++
// 【闭包中的i在后面有修改,所以闭包捕获的是变量i的地址】
// 所以闭包执行时,i的值为3
// struct { F uintptr; i *int }
defer func() {
fmt.Println("result2 =>", i*2)
}()
i++
// Output:
// result2 => 6
// result1 => 2
}
|
defer执行顺序是先进后出#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package main
import "fmt"
func main() {
defer fmt.Print(" !!! ")
defer fmt.Print(" world ")
fmt.Print("hello ")
// hello <- world <- !!!
// 注册顺序:<-----------------------
// 执行顺序:----------------------->
// Output:
// hello world !!!
}
|
defer可以读取函数的有名返回值#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
package main
import "fmt"
func main() {
fmt.Println("result2 =>", fun1())
// Output:
// result2 => 11
}
func fun1() (i int) {
// 堆分配 struct { F uintptr; i *int }
defer func() {
i = i + 10
}()
return 1
}
|
汇编代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
TEXT main.fun1(SB) /mnt/hgfs/workspace/helium/main.go
func fun1() (i int) {
0x4552e0 493b6610 CMPQ 0x10(R14), SP
0x4552e4 0f868a000000 JBE 0x455374
0x4552ea 4883ec78 SUBQ $0x78, SP
0x4552ee 48896c2470 MOVQ BP, 0x70(SP)
0x4552f3 488d6c2470 LEAQ 0x70(SP), BP
0x4552f8 48c744240800000000 MOVQ $0x0, 0x8(SP) # i int
defer func() {
0x455301 440f117c2460 MOVUPS X15, 0x60(SP)
0x455307 488d4c2460 LEAQ 0x60(SP), CX
0x45530c 48894c2458 MOVQ CX, 0x58(SP)
0x455311 8401 TESTB AL, 0(CX)
# funcval.fn
0x455313 488d1566000000 LEAQ main.fun1.func1(SB), DX
0x45531a 4889542460 MOVQ DX, 0x60(SP)
0x45531f 8401 TESTB AL, 0(CX)
# *int 捕获的是有名返回值地址
0x455321 488d542408 LEAQ 0x8(SP), DX
0x455326 4889542468 MOVQ DX, 0x68(SP) # funcval.data=i *int
0x45532b 48894c2428 MOVQ CX, 0x28(SP) # defer.fn=&funcval
0x455330 488d442410 LEAQ 0x10(SP), AX # AX=0x10(SP)=&_defer
# AX 寄存器存储的是runtime.deferprocStack()函数的参数 *_defer
0x455335 e8c650fdff CALL runtime.deferprocStack(SB)
# panic流程recover后会恢复到这里,并把AX置为1。
# 正常情况下AX置为0的。
0x45533a 85c0 TESTL AX, AX
0x45533c 7522 JNE 0x455360
0x45533e 6690 NOPW
0x455340 eb00 JMP 0x455342
return 1
0x455342 48c744240801000000 MOVQ $0x1, 0x8(SP)
0x45534b e8d056fdff CALL runtime.deferreturn(SB)
0x455350 488b442408 MOVQ 0x8(SP), AX
0x455355 488b6c2470 MOVQ 0x70(SP), BP
0x45535a 4883c478 ADDQ $0x78, SP
0x45535e c3 RET
0x45535f 90 NOPL
defer func() {
0x455360 e8bb56fdff CALL runtime.deferreturn(SB)
0x455365 488b442408 MOVQ 0x8(SP), AX
0x45536a 488b6c2470 MOVQ 0x70(SP), BP
0x45536f 4883c478 ADDQ $0x78, SP
0x455373 c3 RET
func fun1() (i int) {
0x455374 e887ccffff CALL runtime.morestack_noctxt.abi0(SB)
0x455379 e962ffffff JMP main.fun1(SB)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
TEXT main.fun1.func1(SB) /mnt/hgfs/workspace/helium/main.go
defer func() {
0x455380 4883ec10 SUBQ $0x10, SP
0x455384 48896c2408 MOVQ BP, 0x8(SP)
0x455389 488d6c2408 LEAQ 0x8(SP), BP
0x45538e 488b4208 MOVQ 0x8(DX), AX # AX=i int
0x455392 48890424 MOVQ AX, 0(SP)
i += 10
0x455396 4883000a ADDQ $0xa, 0(AX)
}()
0x45539a 488b6c2408 MOVQ 0x8(SP), BP
0x45539f 4883c410 ADDQ $0x10, SP
0x4553a3 c3 RET
|
- 由于在
Go
语言中,return
语句不是原子操作,return
等函数执行defer
和销毁栈等操作。
- 最先是所有返回值在进入函数时都会初始化为其类型的零值(姑且称为
ret
赋值)
- 退出时先给返回值赋值
- 然后执行defer命令
- 最后才是
return
操作,return操作包含两个步骤,一是给被调用栈的返回值赋值,然后执行defer注册函数,二是把被调函数的栈的返回值返回给调用函数
返回值=xxx
|
v
调用defer()
|
v
return
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
package main
import "fmt"
func main() {
fmt.Println("=====================================")
fmt.Println("fun1 return:", fun1()) // fun1 return:2
fmt.Println("=====================================")
fmt.Println("fun2 return:", fun2()) // fun2 return:0
fmt.Println("=====================================")
fmt.Println("fun3 return:", fun3()) // fun3 return:5
fmt.Println("=====================================")
fmt.Println("fun4 return:", fun4()) // fun4 return:19
// Output:
// =====================================
// fun1 defer1:1
// fun1 defer2:2
// fun1 return:2
// =====================================
// fun2 defer1:1
// fun2 defer2:2
// fun2 return:0
// =====================================
// fun3 defer:10
// fun3 return:5
// =====================================
// fun4 defer:8
// fun4 return:19
}
func fun1() (i int) {
// struct { F uintptr; i *int }
defer func() {
i++
fmt.Println("fun1 defer2:", i) // fun1 defer2:2
}()
// struct { F uintptr; i *int }
defer func() {
i++
fmt.Println("fun1 defer1:", i) // fun1 defer1:1
}()
// 这里也是给i赋值
// 当做i=0;return;
return 0
}
func fun2() int {
var i int
// struct { F uintptr; i *int }
defer func() {
i++
fmt.Println("fun2 defer2:", i) // fun2 defer2: 2
}()
// struct { F uintptr; i *int }
defer func() {
i++
fmt.Println("fun2 defer1:", i) // fun2 defer1: 1
}()
return i
}
func fun3() (r int) {
t := 5
// struct { F uintptr; t *int }
defer func() {
t = t + 5
fmt.Println("fun3 defer:", t) // fun3 defer:10
}()
return t
}
func fun4() int {
i := 8
// struct { F uintptr, i int }
// 因为是显示传参
defer func(i int) {
fmt.Println("fun4 defer:", i) // fun4 defer:8
}(i)
i = 19
return i
}
|
- 匿名返回值。
defer计算函数执行时间#
- 根据
defer
延迟执行的特性,可以利用它来计算代码块的执行时间。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package main
import (
"fmt"
"time"
)
func main() {
defer timeCost(time.Now()) // 这里利用参数是实时解析的
fmt.Println("start ...")
time.Sleep(time.Second * 5)
fmt.Println("finish ...")
// Output:
// start ...
// finish ...
// 5.0068172s
}
func timeCost(start time.Time) {
terminal := time.Since(start) // now - start
fmt.Println(terminal)
}
|