跳转到主要内容
模块 3 · 第 5 课(共 5)课程中的15/50~12 min
模块课程(5/5)

`defer`:保证清理

defer 推迟调用的执行直到封闭函数 返回。即使函数因恐慌退出它也会运行。多个 defer 在同一函数中按后进先出顺序执行(后进先出)。

这是资源清理的惯用机制:关闭文件, 解锁互斥体、释放连接、恢复状态。

语法和规范用例

Go
f, err := os.Open(path)
if err != nil {
    return err
}
defer f.Close()   // chiamata SEMPRE all'uscita della funzione

// ... usa f liberamente ...
return nil

典型:f.Close()、mu.Unlock()、db.Close()、tx.Rollback()、 recover()。

立即评估论点,推迟执行

当您编写 defer foo(x) 时, x 会在 defer,不在执行时:

Go
x := 10
defer fmt.Println(x)   // cattura 10
x = 99
// quando la funzione ritorna stampa "10", non "99"

后进先出顺序

延迟堆叠起来并从最近的开始执行:

Go
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
// output: 3, 2, 1

这与资源语义一致:最后打开的就是最先打开的 被关闭。

defer + recover:处理恐慌

defer 是拦截 panic 的唯一方法:

Go
func safe() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("recuperato:", r)
        }
    }()
    panic("boom")
}

陷阱:循环中的 defer

将 defer f.Close() 放入循环中会累积延迟 直到结束 函数,而不是每次迭代的函数。在一个长循环中,您打开了许多文件 并且仅在函数退出时关闭它们:文件描述符泄漏的风险。

Go
// MALE: defer accumulati
for _, path := range paths {
    f, _ := os.Open(path)
    defer f.Close()   // chiude TUTTI alla fine della funzione esterna
    // ...
}

// MEGLIO: estrai in una funzione
for _, path := range paths {
    elabora(path)   // ogni chiamata ha il proprio scope di defer
}

尝试一下

锻炼#go.m3.l5.e1
尝试:0加载中...

打印“inizio”,然后用 defer 推迟“fine”的打印,然后打印“mezzo”。输出必须是:inizio/mezzo/fine。

正在加载编辑器...
显示提示

当 main() 即将返回时,延迟运行。

3 次尝试后可用的解决方案

锻炼#go.m3.l5.e2
尝试:0加载中...

编写三个连续的延迟,在代码中按顺序打印 1、2 和 3。输出将为 3 / 2 / 1 (LIFO)。

正在加载编辑器...
显示提示

依次推迟三个;请记住它们以相反的顺序执行。

3 次尝试后可用的解决方案

Quiz#go.m3.l5.e3
准备好

这打印什么? (注意:延迟参数立即计算)

Go
x := 10
defer fmt.Println(x)
x = 99
答案选项

回顾

  • defer call() 推迟执行直到函数返回。
  • 多次延期 = LIFO 顺序。
  • 立即评估参数,推迟执行。
  • 用于确定性清理:文件、互斥体、连接。
  • defer + recover() 拦截 panic (罕见,在边界处)。
  • 切勿在长循环内使用 defer :它会累积直到外部函数返回。