defer是Go语言的一个特色功能,但它的执行顺序常常让人困惑。你以为defer是按照声明顺序执行的?那你就错了。
我曾在代码review时看到了这样一段代码:
func main() {
fmt.Println("开始")
defer fmt.Println("defer 1")
defer fmt.Println("defer 2")
defer fmt.Println("defer 3")
fmt.Println("结束")
}
我问他:"你觉得输出是什么?"
他自信地说:"当然是按照声明顺序执行啊!"
结果运行后发现:
开始
结束
defer 3
defer 2
defer 1
他一脸懵逼:"为什么是倒序执行?"
defer遵循后进先出(LIFO)的原则,就像栈一样:
func demonstrateDeferOrder() {
fmt.Println("函数开始")
defer fmt.Println("defer 1")
defer fmt.Println("defer 2")
defer fmt.Println("defer 3")
fmt.Println("函数结束")
}
输出:
函数开始
函数结束
defer 3
defer 2
defer 1
defer语句在声明时就注册了,但执行时才计算参数值:
func deferTiming() {
x := 1
defer fmt.Println("defer x:", x) // 注册时x=1
x = 2
defer fmt.Println("defer x:", x) // 注册时x=2
x = 3
fmt.Println("x:", x)
}
输出:
x: 3
defer x: 2
defer x: 1
func loopDeferTrap() {
for i := 0; i < 3; i++ {
defer fmt.Println("defer i:", i)
}
fmt.Println("循环结束")
}
输出:
循环结束
defer i: 2
defer i: 1
defer i: 0
陷阱:如果你想要在循环中延迟执行某些操作,defer会在函数结束时才执行,而不是在每次循环迭代结束时。
func deferParameterTrap() {
x := 1
defer func(val int) {
fmt.Println("defer val:", val)
}(x) // 传递x的值
x = 2
defer func() {
fmt.Println("defer x:", x) // 闭包,引用x的地址
}()
x = 3
fmt.Println("x:", x)
}
输出:
x: 3
defer x: 3
defer val: 1
func deferReturnTrap() (result int) {
defer func() {
result++ // 修改命名返回值
}()
return 1
}
func main() {
fmt.Println("返回值:", deferReturnTrap()) // 输出: 2
}
func errorHandlingTrap() error {
file, err := os.Open("nonexistent.txt")
if err != nil {
return err
}
defer file.Close() // 这里会panic!
// 处理文件
return nil
}
如果文件打开失败,file是nil,defer file.Close()会panic。
正确的做法:
func correctErrorHandling() error {
file, err := os.Open("nonexistent.txt")
if err != nil {
return err
}
defer func() {
if file != nil {
file.Close()
}
}()
// 处理文件
return nil
}
func measureTime() {
defer func(start time.Time) {
fmt.Printf("函数执行时间: %v
", time.Since(start))
}(time.Now())
// 模拟一些工作
time.Sleep(100 * time.Millisecond)
}
func resourceCleanup() error {
// 获取资源
resource1, err := acquireResource1()
if err != nil {
return err
}
defer func() {
if resource1 != nil {
resource1.Release()
}
}()
resource2, err := acquireResource2()
if err != nil {
return err
}
defer func() {
if resource2 != nil {
resource2.Release()
}
}()
// 使用资源
return nil
}
func stateRecovery() {
defer func() {
if r := recover(); r != nil {
fmt.Printf("恢复状态: %v
", r)
// 执行恢复逻辑
}
}()
// 可能panic的代码
panic("something went wrong")
}
func conditionalDefer() {
shouldCleanup := true
if shouldCleanup {
defer func() {
fmt.Println("执行清理")
}()
}
// 其他逻辑
}
func benchmarkDefer() {
// 不使用defer
func() {
start := time.Now()
// 一些工作
_ = time.Since(start)
}()
// 使用defer
func() {
defer func(start time.Time) {
_ = time.Since(start)
}(time.Now())
// 一些工作
}()
}
defer有必定的性能开销,在性能敏感的代码中需要谨慎使用。
// 不好的做法
func badLoopDefer() {
for i := 0; i < 1000; i++ {
defer fmt.Println(i) // 所有defer会在函数结束时执行
}
}
// 好的做法
func goodLoopDefer() {
for i := 0; i < 1000; i++ {
func() {
defer fmt.Println(i) // 每次循环迭代结束时执行
}()
}
}
func databaseTransaction() error {
tx, err := db.Begin()
if err != nil {
return err
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
panic(r)
}
}()
// 执行数据库操作
if err := doDatabaseWork(tx); err != nil {
tx.Rollback()
return err
}
return tx.Commit()
}
func handleRequest(w http.ResponseWriter, r *http.Request) {
// 记录请求开始时间
defer func(start time.Time) {
log.Printf("请求处理时间: %v", time.Since(start))
}(time.Now())
// 处理请求
// ...
}
func processFile(filename string) error {
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close()
// 处理文件
// ...
return nil
}
func debugDefer() {
defer func() {
fmt.Println("函数结束")
}()
fmt.Println("函数开始")
defer func() {
fmt.Println("中间defer")
}()
fmt.Println("中间逻辑")
}
func traceExecution() {
defer func() {
fmt.Println("退出函数")
}()
fmt.Println("进入函数")
if someCondition {
defer func() {
fmt.Println("条件分支结束")
}()
fmt.Println("条件分支")
return
}
fmt.Println("正常结束")
}
// 好的使用场景
func goodDeferUsage() {
// 资源清理
defer resource.Release()
// 错误恢复
defer func() {
if r := recover(); r != nil {
log.Printf("恢复: %v", r)
}
}()
// 性能测量
defer func(start time.Time) {
log.Printf("执行时间: %v", time.Since(start))
}(time.Now())
}
// 避免在循环中使用defer
func avoidLoopDefer() {
for i := 0; i < 10; i++ {
func() {
defer fmt.Println(i)
// 处理逻辑
}()
}
}
// 避免defer中的panic
func avoidDeferPanic() {
defer func() {
if r := recover(); r != nil {
log.Printf("defer中的panic: %v", r)
}
}()
// 可能panic的代码
}
defer是Go语言的一个强劲特性,但需要正确理解其执行机制:
记住:defer的执行顺序是后进先出,这是Go语言设计的核心特性。