• GO语言中间的derfer


    defer

    Go语言中有种不错的设计,即延迟(defer)语句,你可以在函数中添加多个defer语句。当函数执行到最后时,这些defer语句会按照逆序执行, 最后该函数返回。特别是当你在进行一些打开资源的操作时,遇到错误需要提前返回,在返回前你需要关闭相应的资源,不然很容易造成资源泄露等问题。如下代码 所示,我们一般写打开一个资源是这样操作的:

    func ReadWrite() bool {
        file.Open("file")
    // 做一些工作
        if failureX {
            file.Close()
            return false
        }
    
        if failureY {
            file.Close()
            return false
        }
    
        file.Close()
        return true
    }
    

    我们看到上面有很多重复的代码,Go的defer有效解决了这个问题。使用它后,不但代码量减少了很多,而且程序变得更优雅。在defer后指定的函数会在函数退出前调用。

    func ReadWrite() bool {
        file.Open("file")
        defer file.Close()
        if failureX {
            return false
        }
        if failureY {
            return false
        }
        return true
    }
    

    如果有很多调用defer,那么defer是采用后进先出模式,所以如下代码会输出4 3 2 1 0

    for i := 0; i < 5; i++ {
        defer fmt.Printf("%d ", i)
    }


    defer 给我的第一印象就是,类似Java中的

    try {

    }finally {

    }

    我目前的理解就是,在函数块中使用defer,就是函数对应的有一个栈空间,先进后出。需要函数结束后调用栈,来出发defer操作。

    如果,一个对象的创建,很消耗内存,需要及时关闭,defer无法像try finnaly哪样准确。

    1. package main  
    2.   
    3. import "fmt"  
    4. import "time"  
    5.   
    6. type User struct {  
    7.         username string  
    8. }  
    9.   
    10. func (this *User) Close() {  
    11.         fmt.Println(this.username, "Closed !!!")  
    12. }  
    13.   
    14. func main() {  
    15.         u1 := &User{"jack"}  
    16.         defer u1.Close()  
    17.         u2 := &User{"lily"}  
    18.         defer u2.Close()  
    19.   
    20.         time.Sleep(10 * time.Second)  
    21.   
    22.         fmt.Println("Done !")  
    23.   
    24. }  
    25. [vicky@localhost goroutine]$  


    [vicky@localhost goroutine]$ go run deferTest1.go
    Done !
    lily Closed !!!
    jack Closed !!!
    [vicky@localhost goroutine]$


    实际上,线程Sleep的10秒,u1,和u2早就可以Close()了,但却需要依赖main()函数的结束,才能defer执行。

    那么尝试给defer添加内部代码区:


    1. package main  
    2.   
    3. import "fmt"  
    4. import "time"  
    5.   
    6. type User struct {  
    7.         username string  
    8. }  
    9.   
    10. func (this *User) Close() {  
    11.         fmt.Println(this.username, "Closed !!!")  
    12. }  
    13.   
    14. func main() {  
    15.         {  
    16.                 // 即便加了代码快范围,依旧也要主函数体结束才执行defer  
    17.                 u1 := &User{"jack"}  
    18.                 defer u1.Close()  
    19.         }  
    20.         u2 := &User{"lily"}  
    21.         defer u2.Close()  
    22.   
    23.         time.Sleep(10 * time.Second)  
    24.   
    25.         fmt.Println("Done !")  
    26.   
    27. }  


    Done !
    lily Closed !!!
    jack Closed !!!
    [vicky@localhost goroutine]$


    依旧defer的执行在Done!后。那么如何才能达到try finally 哪样准确的Close呢?

    1. package main  
    2.   
    3. import "fmt"  
    4. import "time"  
    5.   
    6. type User struct {  
    7.         username string  
    8. }  
    9.   
    10. func (this *User) Close() {  
    11.         fmt.Println(this.username, "Closed !!!")  
    12. }  
    13.   
    14. func main() {  
    15.         u1 := &User{"jack"}  
    16.         f(u1) // 这样的方式,u1才会不依赖main函数的执行  
    17.   
    18.         // 这样的方式,u2也不会依赖main函数的执行  
    19.         u2 := &User{"lily"}  
    20.         // m := func() {  
    21.         //         defer u2.Close()  
    22.         //         // u2 do somthing  
    23.         // }  
    24.         // m()<pre name="code" class="plain">        func() {  
    25.                  defer u2.Close()  
    26.                  // u2 do somthing  
    27.         }()</pre>        time.Sleep(10 * time.Second)        fmt.Println("Done !")}func f(u *User) {        defer u.Close()        // u1 do gomething}  


    [vicky@localhost goroutine]$ go run deferTest3.go
    jack Closed !!!
    lily Closed !!!
    Done !


    这样的使用方式,视乎不太合理,但却有存在的必要性。大多数情况下,可以用于 u1,u2  之类非常消耗内存,或者cpu,其后执行时间过程且没有太多关联的情况。既保留了defer的功能特性,也满足范围精确控制的条件!

  • 相关阅读:
    Java初学者笔记四:按行读写文件和输入处理
    Java初学者笔记三:关于字符串和自实现数组常见操作以及异常处理
    Java初学者笔记二:关于类的常见知识点汇总
    python的类继承与派生
    Java初学者笔记一:元类、获取类型、枚举
    Tomcat远程任意代码执行漏洞及其POC(CVE-2017-12617)
    PostgreSQL远程代码执行漏洞(CVE-2018-1058)学习笔记
    python的三个函数(eval、exec、complie)和python版RMI
    关于Memcached反射型DRDoS攻击分析
    python的其他安全隐患
  • 原文地址:https://www.cnblogs.com/zhangym/p/5580113.html
Copyright © 2020-2023  润新知