defer 的三个规则:

  1. 规则一:defer 声明时,其后面函数参数会被实时解析
  2. 规则二:defer 执行顺序为先进后出(FILO)(First Insert Last Out)。
  3. 规则三: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

  1. 由于在Go语言中,return语句不是原子操作,return等函数执行defer和销毁栈等操作。
  2. 最先是所有返回值在进入函数时都会初始化为其类型的零值(姑且称为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
}
  1. 匿名返回值

defer计算函数执行时间

  1. 根据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)
}