defer
作者: ryan 发布于: 2025/7/28 更新于: 2025/7/28 字数: 0 字 阅读: 0 分钟
defer
是 Go 语言中用于延迟执行的特殊关键字,它会将函数调用推入一个栈中,在当前函数返回(正常结束或 panic)时,按照后进先出(LIFO) 的顺序执行这些调用。
package main
import "fmt"
func main() {
fmt.Println("start") // 1. 立即执行
defer fmt.Println("1") // 2. 压入 defer 栈(栈底)
defer fmt.Println("2") // 3. 压入 defer 栈(中间)
defer fmt.Println("3") // 4. 压入 defer 栈(栈顶)
fmt.Println("end") // 5. 立即执行
} // 6. 函数结束,逆序执行 defer
start
end
3 // 最后声明的 defer 最先执行
2
1 // 最先声明的 defer 最后执行
defer 的执行时机
与panic的交互逻辑
panic
触发后函数会立即停止后续代码执行,转而执行已注册的defer
链 未注册的defer
不执行panic
后的defer
不会被注册(如示例中defer fmt.Println(3)
被跳过)
func main() {
fmt.Println("Start") // 1. 正常输出:Start
defer fmt.Println(1) // 注册第1个defer
defer fmt.Println(2) // 注册第2个defer
panic("出错了") // 触发panic,后续代码终止
defer fmt.Println(3) // 不会被注册(因panic后中断)
fmt.Println("End") // 不会执行
}
输出:
Start
2
1
panic: 出错了
...(错误堆栈信息)
os.Exit() 的特殊性
调用 os.Exit(code)
时,程序立即终止,不执行任何 defer
,也不触发 panic
处理流程
func main() {
fmt.Println("Start") // 1. 输出:Start
defer fmt.Println(1) // 注册 defer 1
defer fmt.Println(2) // 注册 defer 2
defer fmt.Println(3) // 注册 defer 3
fmt.Println("End") // 2. 输出:End
os.Exit(100) // 程序立即终止,不执行任何 defer!
}
输出:
Start
End
程序退出状态码为 100,由 os.Exit(100)
指定。因为 os.Exit(100)
直接终止进程,跳过所有 defer
defer fmt.Println(1/2/3)
均未执行。
参数预计算
注册时固定参数:defer fmt.Println(count)
中的 count
在 defer
语句注册时立即被计算并固定,而非函数退出时动态取值
func main() {
count := 1 // 1. 初始化 count = 1
fmt.Println("start") // 2. 输出: start
defer fmt.Println(count) // 3. 注册 defer 1:固定 count=1(立刻计算参数值)
count++ // 4. count = 2
defer fmt.Println(count) // 5. 注册 defer 2:固定 count=2(立刻计算参数值)
count++ // 6. count = 3
defer fmt.Println(count) // 7. 注册 defer 3:固定 count=3(立刻计算参数值)
fmt.Println("end") // 8. 输出: end
} // 9. 函数退出,按 LIFO 执行 defer
输出:
start
end
3
2
1
闭包匿名函数参数计算时机
闭包形式(如 defer func() { fmt.Println(count) }()
),动态捕获变量最新值(需要在函数执行时计算)
func main() {
count := 1 // 初始化 count=1
fmt.Println("start") // 输出: start
// 闭包形式(未固定参数)
defer func() {
fmt.Println(count) // 最终输出: 3(动态捕获最新值)
}()
count++ // count=2
// 函数调用形式(参数预计算)
defer fmt.Println(count) // 输出: 2(注册时固定为2)[1][2][6]
count++ // count=3
// 函数调用形式(参数预计算)
defer fmt.Println(count) // 输出: 3(注册时固定为3)[1][2][6]
fmt.Println("end") // 输出: end
}
最终输出:
start // 直接执行
end // 直接执行
3 // 后注册的固定值 defer(count=3)
2 // 先注册的固定值 defer(count=2)
3 // 闭包 defer(动态捕获 count=3)
带参数的匿名函数defer func(count int) { ... }(count)
在注册时,立即计算参数值 count
(此时为1),创建参数副本传递给匿名函数(值传递) 因此后续的 count++
不影响该副本的值。
func main() {
count := 1 // 初始化 count = 1
fmt.Println("start") // 1. 输出: start
// 带参数的匿名函数 defer
defer func(count int) { // 3. 注册时立即计算并复制 count 的值
fmt.Println(count) // 输出: 1(使用注册时的副本值)
}(count) // 传递 count 的值(此时 count=1)
count++ // 4. count = 2
defer fmt.Println(count) // 5. 注册 defer 并固定参数值: 2
count++ // 6. count = 3
defer fmt.Println(count) // 7. 注册 defer 并固定参数值: 3
fmt.Println("end") // 8. 输出: end
} // 9. 函数退出,逆序执行 defer
最终输出:
start
end
3 // 最后注册的 defer (count=3)
2 // 中间的 defer (count=2)
1 // 带参数的匿名函数 (count 副本值=1)
不使用实参 c 匿名函数 func(c int) { ... }(count)
中的参数 c
是注册时的值拷贝(c=1
),但函数体内部的 fmt.Println(count)
未使用参数 c
,而是直接引用外部变量 count
(闭包捕获) count
在执行时(main
函数返回前)才确定,此时 count
已递增至 3
。
func main() {
count := 1
fmt.Println("start")
defer func(c int) {
fmt.Println(count) // 闭包引用外部变量count,非参数c
}(count) // 注册时参数c=1(值拷贝)
count++
defer fmt.Println(count) // 注册时固定参数:count=2
count++
defer fmt.Println(count) // 注册时固定参数:count=3
fmt.Println("end")
}
最终输出:
start
end
3 // 最后注册的defer,参数固定为3
2 // 第二个defer,参数固定为2
3 // 闭包捕获最新count(此时count=3)