Skip to content

defer

作者: ryan 发布于: 2025/7/28 更新于: 2025/7/28 字数: 0 字 阅读: 0 分钟

defer 是 Go 语言中用于延迟执行的特殊关键字,它会将函数调用推入一个栈中,在当前函数返回(正常结束或 panic)时,按照后进先出(LIFO) 的顺序执行这些调用。

go
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)被跳过)

go
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")        // 不会执行
}

输出

go
Start
2
1
panic: 出错了
...(错误堆栈信息)

os.Exit() 的特殊性

调用 os.Exit(code) 时,程序立即终止,不执行任何 defer,也不触发 panic 处理流程

go
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!
}

输出

go
Start
End

程序退出状态码为 100,由 os.Exit(100) 指定。因为 os.Exit(100) 直接终止进程,跳过所有 deferdefer fmt.Println(1/2/3) 均未执行。

参数预计算

注册时固定参数defer fmt.Println(count) 中的 count 在 defer 语句注册时立即被计算并固定,而非函数退出时动态取值

go
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

输出:

go
start
end
3
2
1

闭包匿名函数参数计算时机

闭包形式(如 defer func() { fmt.Println(count) }()),动态捕获变量最新值(需要在函数执行时计算)

go
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
}

最终输出

go
start     // 直接执行
end       // 直接执行
3         // 后注册的固定值 defer(count=3)
2         // 先注册的固定值 defer(count=2)
3         // 闭包 defer(动态捕获 count=3)

带参数的匿名函数defer func(count int) { ... }(count) 在注册时,立即计算参数值 count(此时为1),创建参数副本传递给匿名函数(值传递) 因此后续的 count++ 不影响该副本的值。

go
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

最终输出

go
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

go
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")
}

最终输出

go
start
end
3  // 最后注册的defer,参数固定为3
2  // 第二个defer,参数固定为2
3  // 闭包捕获最新count(此时count=3)