defer

本篇讲解 Go defer 的延迟执行机制、执行顺序、参数求值时机与栈变化流程,并给出可运行示例。

字数 1191 字

defer(关键字)

它是做什么的

defer 用于注册“延迟执行”的函数调用。
这些调用会在当前函数即将返回前执行,常用于资源清理(如关闭文件、解锁、释放连接)。

语法/规则

  1. defer 语句会先登记,等函数返回前再执行。
  2. 一个函数内有多个 defer 时,执行顺序是后进先出(LIFO)。
  3. defer 调用中的参数在“登记 defer 的那一刻”就会求值。
  4. defer 常与 return、错误处理一起使用,保证清理逻辑稳定执行。

多个 defer 执行顺序示例

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

import "fmt"

func Func() {
	defer fmt.Println("defer2")
	fmt.Println("func")
	defer fmt.Println("defer1")
}

func main() {
	defer fmt.Println("defer4")
	Func()
	defer fmt.Println("defer3")
}

输出结果:

1
2
3
4
5
func
defer1
defer2
defer3
defer4

从栈角度理解 defer

理解这个示例的关键是:
每个函数调用帧都有自己的 defer 栈,互不干扰。
谁先返回,就先清空谁自己的 defer 栈(后进先出)。

为了便于观察,下面把栈都写成“栈顶 -> 栈底”。

逐行 + 栈变化(核心示例)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
func Func() {
	defer fmt.Println("defer2")
	fmt.Println("func")
	defer fmt.Println("defer1")
}

func main() {
	defer fmt.Println("defer4")
	Func()
	defer fmt.Println("defer3")
}

步骤拆解如下(栈写法:栈顶 -> 栈底):

  1. 进入 main,执行 defer fmt.Println("defer4")
    main 栈:[defer4]
    Func 栈:[]
    当前输出:

  2. 调用 Func(),进入 Func 后执行 defer fmt.Println("defer2")
    main 栈:[defer4]
    Func 栈:[defer2]
    当前输出:

  3. 执行 fmt.Println("func")
    main 栈:[defer4]
    Func 栈:[defer2]
    当前输出:func

  4. 执行 defer fmt.Println("defer1")
    main 栈:[defer4]
    Func 栈:[defer1, defer2]
    当前输出:func

  5. Func 返回,开始弹出 Func 的 defer 栈(后进先出)。
    先执行 defer1,再执行 defer2
    main 栈:[defer4]
    Func 栈:[]
    当前输出:func -> defer1 -> defer2

  6. 回到 main,执行 defer fmt.Println("defer3")
    main 栈:[defer3, defer4]
    Func 栈:[]
    当前输出:func -> defer1 -> defer2

  7. main 返回,开始弹出 main 的 defer 栈(后进先出)。
    先执行 defer3,再执行 defer4
    main 栈:[]
    Func 栈:[]
    当前输出:func -> defer1 -> defer2 -> defer3 -> defer4

最终输出就是:

1
2
3
4
5
func
defer1
defer2
defer3
defer4

你可以记一句话:
defer 语句只是入栈,函数返回时才出栈执行;同一函数内严格后进先出。

参数求值时机示例

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

import "fmt"

func main() {
	value := 1
	defer fmt.Println("defer value:", value)

	value = 2
	fmt.Println("current value:", value)
}

输出结果:

1
2
current value: 2
defer value: 1

参数求值流程分解

defer fmt.Println("defer value:", value) 里的 value,在声明 defer 那一刻就被确定为 1
所以后面即使把 value 改成 2,延迟调用里仍然打印的是当时保存的值。

用“栈”来理解就是:
defer 入栈时,调用参数已经打包好放进栈节点里了,不会等到出栈时再重新取最新变量值。

常见错误

  1. 误以为多个 defer 按书写顺序执行,实际是后写先执行。
  2. 误以为 defer 参数在函数结束时才求值,实际在声明时就求值。
  3. 在循环里大量使用 defer 却不关注性能和释放时机,可能造成资源堆积。
  4. 把不同函数作用域的 defer 执行顺序混在一起判断,导致输出顺序理解错误。
使用 Hugo 构建
主题 StackJimmy 设计 由 Hobin 魔改
载入天数...载入时分秒...
发表了 0 篇文章 · 发表了 46 篇笔记 · 总计 2 万 5 千字(其中笔记 25104 字)