wx

关注公众号领取Go学习资料

wx

洋葱小店

Go语言流程控制:Defer


在 Golang 中,defer 是一个关键字,用于定义延迟执行函数。它可以将一个函数推迟到当前函数返回之后执行,即使该函数出现异常或错误也不会影响 defer 定义的代码块。

常见用例

以下是 defer 的一些常见用例: 1、关闭文件资源 使用 defer 关闭文件资源是一种非常常见的方法,可以确保无论代码中发生了什么,文件都能够被关闭。

f, err := os.Open("/path/to/file")
if err != nil {
    log.Fatal(err)
}
defer f.Close()

2、记录耗时 我们可以在开始处理某个请求前调用 time.Now() 函数来记录起始时间,然后在退出函数时计算经过的时间并打印出来。

func processRequest(req *http.Request) {
    start := time.Now()
    defer logTime(start)

    // 处理请求
}

func logTime(start time.Time) {
    log.Printf("request took %v", time.Since(start))
}

3、锁的释放 当我们在使用互斥锁时,必须确保每次获取锁后都要及时释放锁,否则会导致死锁的情况。使用 defer 语句可以确保在任何情况下都会释放锁。

var mu sync.Mutex

func someFunc() {
    mu.Lock()
    defer mu.Unlock()

    // 执行一些需要锁定的操作
}

注意事项

在使用 defer 时,需要注意以下几个问题:

  1. 延迟执行的函数参数会在定义时被计算,而不是在实际执行时计算。这意味着如果参数是一个指针或一个引用类型,并且在函数退出之前被修改了,那么这些修改会影响到延迟执行的函数。例如:
func someFunc() {
    var a = 10
    defer fmt.Println(a)
    a = 20
}
// 输出结果为 10
  1. 如果有多个 defer 语句,它们的执行顺序与声明的顺序相反。也就是说,最后一个 defer 语句将首先执行,最先声明的 defer 语句将最后执行。
func someFunc() {
    defer fmt.Println("first")
    defer fmt.Println("second")
    defer fmt.Println("third")
}
// 输出结果为 third, second, first
  1. 在函数中,return 语句并不是原子操作。具体来说,它分为两个步骤:第一步是将返回值赋给调用者;第二步是执行函数的清理工作(包括 defer 语句)。因此,在使用 defer 时,需要注意不要让 defer 影响函数的返回值。例如:
func someFunc() (result int) {
    defer func() {
        result++
    }()
    return 0
}
// 返回值为 1
  1. 如果在 defer 语句中使用了一个函数返回的错误值并且该函数可能会失败,则应该在调用函数后立即检查返回值。否则,在函数最后执行时,可能会忽略错误并导致不可预测的行为。例如:
func someFunc() (err error) {
    file, err := os.Open("filename.txt")
    if err != nil {
        return err
    }
    defer func() {
        if err := file.Close(); err != nil {
            log.Fatal(err)
        }
    }()
    // ...
}
如有疑问关注公众号给我留言
wx

关注公众号领取Go学习资料

wx

洋葱小店

©2017-2023 鲁ICP备17023316号-1 Powered by Hugo