Go语言-defer特性和使用场景

Go语言的defer主要用于延迟调用,会在当前函数返回之前执行defer注册的函数。类似其他语言(例如java)中的try...catch...finally语句,利用这个特性可以用来释放资源等操作,下面归纳总结一下defer的基本用法。

一、defer的特性

1、延迟调用

1 func main()  {
2 
3     fmt.Println("start...")
4     defer fmt.Println("defer...")
5     fmt.Println("end...")
6 }

执行结果如下:

start...
end...
defer...

可以看到,defer执行在函数退出之前,需要注意的是:defer必须放在函数内部。

 

2、LIFO(先进后出,栈特性)

func main()  {

    for i:= 0; i < 5; i++ {
        defer fmt.Println(i)
    }
}

执行结果如下:

4
3
2
1
0

通过执行结果可以看到,执行的结果和入栈的顺序是相反的,即后进先出。

 

3、defer的作用域

func main()  {

    func() {
        defer println("defer...")
    }()
    println("end...")
}

执行结果如下:

defer...
end...

可以看到,defer的作用域只在函数内部,因为defer只能是在函数内部,所以不会对函数外执行顺序产生影响。

 

4、异常场景

func main()  {

    defer func() {
        e := recover();
        if e != nil {
            println("defer...")
        }
    }()
    panic("throw panic")
}

执行结果如下:

defer...

可以看到,在panic发生时也能执行,例如在资源泄漏、死锁等场景下特别有用,因为发生panic时程序进程不一定会终止,可能被外层reciver住,这个时候可以利用defer来确保必要的执行。但是使用限制是:defer必须和recover结合使用才有意义。

 

二、defer的使用场景

1、并发同步控制

2、锁场景,用来确保锁释放,在其他语言中一般通过try...catch...finally来确保最终执行,但是Go语言的defer能一更加优雅的代码格式来实现,便于程序的阅读性,同时能防止程序员忘记释放导致出现死锁等问题,如下:

xx.Lock()
defer xx.unLock()

...相关业务代码

这样的代码看起来是不是比在加锁和释放锁之间插入大量的业务代码看起来更加便于理解和维护呢?

3、资源释放,其实跟2的锁场景类似,都是在资源打开后,直接通过defer延迟调用的特性实现资源释放

xx.open(资源)
defer xx.close(资源)

...相关业务代码

 

posted on 2022-04-10 17:06  funnyboy0128  阅读(142)  评论(0编辑  收藏  举报

导航