Skip to content

defer ​

defer 是 Go 语言中的一个关键字,用于延迟执行某个函数调用,直到包含它的函数执行完毕(即函数返回时)。这在资源清理、解锁、关闭文件、网络连接等场景中非常有用。defer 语句在 Go 中具有非常独特的行为,它保证了即使函数发生了错误或提前返回,某些清理操作仍然会被执行。

1. defer 的基本用法 ​

defer 语句通常用于资源的释放或清理工作,它会将一个函数调用推迟到当前函数执行结束时再执行。defer 语句的调用是 LIFO(后进先出)的,这意味着多个 defer 语句会按照相反的顺序执行。

go
package main

import "fmt"

func example() {
    fmt.Println("Start of function")

    // 使用 defer 推迟执行清理操作
    defer fmt.Println("This will be printed last")

    fmt.Println("This will be printed first")
}

func main() {
    example()
}

输出:

Start of function
This will be printed first
This will be printed last

在上面的示例中,defer 语句中的 fmt.Println("This will be printed last") 在 example 函数的其他代码执行完毕后执行。

2. defer 语句的执行顺序 ​

defer 语句按照 后进先出(LIFO) 的顺序执行,这意味着如果在函数中有多个 defer 语句,它们会按照反向顺序执行。

go
package main

import "fmt"

func example() {
    fmt.Println("Start of function")

    // 使用多个 defer 语句
    defer fmt.Println("This will be printed last")
    defer fmt.Println("This will be printed second")
    defer fmt.Println("This will be printed first")

    fmt.Println("This will be printed first during execution")
}

func main() {
    example()
}

输出:

Start of function
This will be printed first during execution
This will be printed first
This will be printed second
This will be printed last

3. defer 和返回值 ​

defer 语句在函数返回之前执行,这意味着即使函数提前返回,defer 语句也会执行。defer 语句可以在返回之前修改返回值。

go
package main

import "fmt"

func example() (result int) {
    defer func() {
        result = 42 // 修改返回值
    }()

    return 0
}

func main() {
    fmt.Println(example()) // 输出 42
}

在这个例子中,defer 语句修改了返回值 result,即使 return 0 在 defer 之前执行,defer 仍然会在返回时修改返回值。

4. defer 与错误处理 ​

defer 常用于清理资源,如关闭文件、解锁互斥锁(mutex)等。它确保了即使函数发生错误或提前返回,资源清理工作仍然可以执行。

go
package main

import (
    "fmt"
    "os"
)

func main() {
    file, err := os.Open("test.txt")
    if err != nil {
        fmt.Println("Error opening file:", err)
        return
    }
    // 确保文件在函数结束时被关闭
    defer file.Close()

    // 执行一些操作...
    fmt.Println("File opened successfully")
}

在上面的示例中,defer file.Close() 确保文件在 main 函数结束时被关闭,无论函数是正常结束还是提前由于错误退出。

5. defer 的性能影响 ​

由于 defer 在函数执行过程中会推迟调用,并在函数返回时执行,理论上它可能会带来一些性能开销。尤其是当在循环或频繁调用的函数中使用时,性能差异可能会更加明显。大多数情况下,defer 带来的开销是微不足道的,除非在非常性能敏感的代码中(例如,在一个大规模的循环中),才需要考虑它的影响。

6. defer 语句的执行时机 ​

  • defer 语句是在当前函数执行完毕、返回之前执行的。
  • defer 中的函数参数会在 defer 被执行时被求值,而不是在定义时被求值。
go
package main

import "fmt"

func example() {
    x := 10

    // 这里参数 x 的值是 10,而不是在 defer 执行时的变化值
    defer fmt.Println(x) 

    x = 20
}

func main() {
    example() // 输出 10,而不是 20
}

7. defer 用于解锁互斥锁 ​

在并发编程中,defer 被广泛应用于确保互斥锁在函数返回时被释放。

go
package main

import (
    "fmt"
    "sync"
)

var mutex sync.Mutex

func example() {
    mutex.Lock()         // 加锁
    defer mutex.Unlock() // 解锁

    fmt.Println("Critical section")
}

func main() {
    example() // 输出:Critical section
}

在这个例子中,defer mutex.Unlock() 确保了在 example 函数结束时无论发生什么情况(即使发生错误或提前返回),互斥锁都能被释放。

总结 ​

  • defer 是 Go 中用于延迟执行的关键字,通常用于资源的释放、清理操作。
  • defer 语句会在函数返回前执行,多个 defer 语句会按照 后进先出 的顺序执行。
  • defer 对于文件关闭、解锁互斥锁、以及其他资源清理非常有用。
  • 虽然 defer 可能带来微小的性能开销,但其带来的代码可读性和安全性通常远超开销,除非在性能敏感的地方。