原创分享 16.defer 让代码更清晰

happy_brother · 2021年01月21日 · 55 次阅读

本文视频地址

一 defer 是什么

日常我们写如下的代码

var mu sync.Mutex
mu.Lock()
count++
mu.Unlock()

这样的代码特点就是在函数中会申请一些资源并在函数退出前释放或关闭资源。函数的实现需要资源在函数退出时被及时地释放,无论函数的执行时按预期进行,还是抛错。为此,程序员需要对函数中的错误里特别关注,在错误处理时不能忘记释放资源。这样让程序员需要思考的问题就会增加。同时多个资源释放怎么办?多个资源抛错怎么办?这样的逻辑将变得十分复杂,代码可读性也会极大降低,那程序的健壮性也是无法保证的。

我们看看 Go 语言中的 defer 如何解决上面的问题。

defer

1 只有在函数(方法)内部才能使用。 2 defer + 函数(方法),这些函数被称为 deferred 函数。defer 将注册到其所在 goroutine 用于存放 deferred 函数的栈数据结构中,这些 deferred 函数将在执行 defer 的函数退出前被按后进先出 (LIFO) 的顺序调度执行。无论是执行到函数尾部返回,还是在某个粗偶处理分支显示 renturn,还是 panic,已经存储到 deferred 函数栈中的函数都会被调度执行。因此,deferred 函数是一个可以在任何情况下都可以为函数进行收尾工作。

var mu sync.Mutex
mu.Lock()
defer mu.Unlock()
count++

上面的例子,我们用 defer 改写一下,只要 Lock 方法被调用,就使用 defer + Unlock 方法让锁和解锁成对出现,逻辑简化更加清晰。

二 defer 的常见用法

下面我们通过 deferred 函数拦截 panic 并恢复了程序,继续运行:

func MakePanic(){
    fmt.Println("制造一个panic")
    panic("111")
}

func Survive() {
    defer func() {
        if e:=recover();e!=nil{
            fmt.Println(e)
            fmt.Println("我来拯救你这个panic")
        }
    }()
    MakePanic()
}

func main() {
    Survive()
}

输出如下:
制造一个panic
111
我来拯救你这个panic

deferred 函数在 panic 的情况下,仍能被执行。

三 defer 避 “坑” 指南

1 哪些函数可以作为 deferred 函数 方法和函数与 defer 无条件的结合,但有返回值的函数或方法,返回值会在 deferred 函数被调用的时候丢弃。

2 记得 defer 是后进先出的顺序执行的

3 defer 也是有性能损失的,为保证 defer 对整个应用的影响,尽量多进行测试,适当的使用 defer。

更多原创文章干货分享,请关注公众号
  • 加微信实战群请加微信(注明:实战群):gocnio
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册