047-Deferred 函数

想象这样一种场景:通常我们在编程的时候,经常会遇到资源申请完成后,再手工释放。比如打开一个文件后,再执行 close 操作关闭文件描述符,如果你不关闭,则会产生描述符泄露。如果程序逻辑复杂,这非常容易出错。因为你的函数可能随时遇到错误而 return 出去,此时你极易忘记 close 掉申请过的资源。

Go 提供了关键字 defer 帮忙我们解决这个问题。它有点像 C++ 里的 RAII 机制,目的都是为了达到自动执行析构(资源释放)函数。

1. deferred 函数

不知道中文译名是什么,但是 defer 这个单词的意思是『推迟、延时』的意思。怎么使用它呢?先来看一个简单的例子:

package main

import "fmt"

// 模拟资源申请
func alloc() {
    fmt.Println("allocate resource")
}

// 模拟资源释放
func free() {
    fmt.Println("free resource")
}

// 测试函数
func test() {
    alloc()
    defer free()
    fmt.Println("test alloc and free")
}

func main() {
    test()
    fmt.Println("end")
}

程序运行后输出:


这里写图片描述
图1 使用 defer 自动执行资源释放函数

这个例子非常简单,完全体现出了 defer 的功能。这里再解释一下 defer 执行时机。

  • 正常情况下:一旦在某个函数或方法 F 中使用 defer 关键字注册了一个函数 f ,那么当函数 F 执行结束时,会自动调用 f.
  • 一旦程序发生 panic 异常(异常位置当然必须得在 defer 关键字后面发生才有用),则会调用 f.
func F(param-list) return-list {
    // ...
    defer f(a, b, ...)
    // ...
    return x, y, ...
}

上面的例子中,你可以认为 f 函数的执行时机是在 return 之后,最后一个花括号之前执行。接下来,我们通过几个示例,来看看 defer 还可以做哪些神奇的事情 。

2. defer 示例

2.1 记录函数运行时间

package main

import (
    "fmt"
    "time"
)

func process() {
    defer trace("process")() // 注意 trace 函数会返回一个新函数,延时执行这个新函数
    time.Sleep(5 * time.Second)
}

func trace(s string) func() {
    start := time.Now()
    fmt.Printf("%s start\n", s)
    return func() {
        fmt.Printf("%s end. elapse:%s\n", s, time.Since(start))
    }
}

func main() {
    process()
}

2.2 记录函数参数和返回值

package main

import (
    "fmt"
)

// 配合有名返回值,defer 可以打印出返回值
func add(x, y int) (z int) {
    defer func() {
        fmt.Printf("input:%d,%d output:%d\n", x, y, z)
    }()
    return x + y
}

func main() {
    add(1, 2)
}

3. defer 的执行顺序

如果一个函数中注册多个 defer 函数,那它们的执行顺序是怎样的呢?看下面的示例你就知道结果了。

package main

import "fmt"

func f1() {
    fmt.Println("f1")
}
func f2() {
    fmt.Println("f2")
}
func f3() {
    fmt.Println("f3")
}

func test() {
    defer f1()
    defer f2()
    defer f3()

    fmt.Println("test")
}

func main() {
    test()
    fmt.Println("end")
}

上面的程序最终会输出:

// Output:
test
f3
f2
f1
end

最后的结论就是:defer 函数的执行顺序和它的注册顺序是相反的,这有点像栈操作。最后一个被注册的函数会最先调用。

4. 总结

  • 掌握 defer 用法
  • 知道 defer 函数的执行时机
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值