August Rush

一个还在努力成长的小火汁!

游龙当归海,海不迎我自来也。

We create our own demons.

You can reach me at augustrush0923@gmail.com
深入理解Go的defer机制
发布:2024年01月08日 | 作者:augustrush | 阅读量: 207

什么是defer

defer语句的主要用途是在执行函数返回之前延迟执行一些操作。这些操作可能包括资源的释放文件的关闭解锁操作等。defer语句可以确保无论函数如何返回(包括发生panic的情况),这些操作都会被执行,从而帮助确保代码的健壮性和可靠性。

defer的特性

defer主要具有两大特性:

  • 延迟调用
  • 后进先出(LIFO)

延迟调用

在当前函数执行完成后再调用defer后的语句执行。

package main

import "fmt"

func main() {
    defer fmt.Println("first defer")
    fmt.Println("main kernel processing...")
}

输出结果:

$ go run main.go
main kernel processing...
first defer

后进先出(LIFO)

当有多个defer语句时,遵循后进先出的原则来执行。

package main

import "fmt"

func main() {
    defer fmt.Println("1. first defer.")
    defer fmt.Println("2. second defer.")
    defer fmt.Println("3. third defer.")
    fmt.Println("main kernel processing...")
}

输出结果:

$ go run main.go
main kernel processing...
3. third defer.
2. second defer.
1. first defer.

defer的执行时机

在Go语言的函数中return语句在底层并不是原子操作,它分为给返回值赋值RET指令两步。而defer语句执行的时机就在返回值赋值操作后,RET指令执行前。

package main

import "fmt"

func f1() int {
    x := 5
    defer func() {
        x++
    }()
    return x //返回值=x -> new(int) = x(此时进行值传递 new(int)会获得x的一个副本) ; 执行defer -> x++(只影响了局部变量x) ; 执行RET
}

func f2() (x int) {
    defer func() {
        x++
    }()
    return 5 // 返回值=x -> x = x ; 执行defer -> x++ ; 执行RET
}

func f3() (y int) {
    x := 5
    defer func() {
        x++
    }()
    return x // 返回值=x -> y = x(此时进行值传递 y会获得x的一个副本); 执行defer -> x++(只影响了局部变量x) ; 执行RET
}
func f4() (x int) {
    defer func(x int) {
        x++
    }(x)
    return 5 // 返回值=5 ; 执行defer -> 接收一个变量x并执行x++(此时接收一个值传递,x++只影响当前局部变量); 执行RET
}

func main() {
    fmt.Println(f1())
    fmt.Println(f2())
    fmt.Println(f3())
    fmt.Println(f4())
}

输出结果:

$ go run main.go
5
6
5
5

延迟函数的参数在defer声明时就决定了

defer声明的时候,需要连同函数地址、函数形参一同压进栈内。所以当参数有需要调用其他函数计算结果的时候,需要先计算出结果后再压进栈。

package main

import "fmt"

func calc(index string, a, b int) int {
    ret := a + b
    fmt.Println(index, a, b, ret)
    return ret
}

func main() {
    x := 1
    y := 2
    defer calc("AA", x, calc("A", x, y))
    x = 10
    defer calc("BB", x, calc("B", x, y))
    y = 20
}

输出结果:

$ go run main.go
A 1 2 3
B 10 2 12
BB 10 12 22
AA 1 3 4

解析:

首先声明并赋值两个变量x和y,分别是1和2

然后注册第一个defer语句的时候,Go需要确定每个函数的变量的值。因此要先调用一次calc函数计算出值,并把这个defer语句压进栈内。此时会输出:

A 1 2 3 

重新给变量x赋值,然后注册第二defer语句的时候,仍然需要确定每个函数的变量的值。再调用calc函数计算出值,并把这个defer语句压进栈内。此时会输出:

B 10 2 12

重新给变量y赋值为20。然后从栈中取出最近压进去的defer语句并执行,此时会输出:

BB 10 12 22

然后再从栈中取出最近压进去的defer语句并执行,此时会输出:

AA 1 3 4

栈内没有defer语句后,执行RET。

当函数内发生panic时,defer仍然会执行完毕

package main

import "fmt"

func main() {
    defer fmt.Println("defer 1")
    defer fmt.Println("defer 2")
    defer fmt.Println("defer 3")

    panic("出错啦")
}

输出结果:

$ go run main.go
defer 3
defer 2
defer 1
panic: 出错啦

无论函数是正常返回还是通过panic返回,defer语句都会执行



  • 标签云

  • 支付宝扫码支持一下

  • 微信扫码支持一下



基于Nginx+Supervisord+uWSGI+Django1.11.1+Python3.6.5构建

京ICP备20007446号-1 & 豫公网安备 41100202000460号

网站地图 & RSS | Feed