16.defer 让代码更清晰

本文视频地址

一 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。

image

你可能感兴趣的:(golang)