zoukankan      html  css  js  c++  java
  • go语言defer使用

    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哪样准确。

    package main
    
    import "fmt"
    import "time"
    
    type User struct {
            username string
    }
    
    func (this *User) Close() {
            fmt.Println(this.username, "Closed !!!")
    }
    
    func main() {
            u1 := &User{"jack"}
            defer u1.Close()
            u2 := &User{"lily"}
            defer u2.Close()
    
            time.Sleep(10 * time.Second)
    
            fmt.Println("Done !")
    
    }
    [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添加内部代码区:

    package main
    
    import "fmt"
    import "time"
    
    type User struct {
            username string
    }
    
    func (this *User) Close() {
            fmt.Println(this.username, "Closed !!!")
    }
    
    func main() {
            {
                    // 即便加了代码快范围,依旧也要主函数体结束才执行defer
                    u1 := &User{"jack"}
                    defer u1.Close()
            }
            u2 := &User{"lily"}
            defer u2.Close()
    
            time.Sleep(10 * time.Second)
    
            fmt.Println("Done !")
    
    }

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

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

    package main
    
    import "fmt"
    import "time"
    
    type User struct {
            username string
    }
    
    func (this *User) Close() {
            fmt.Println(this.username, "Closed !!!")
    }
    
    func main() {
            u1 := &User{"jack"}
            f(u1) // 这样的方式,u1才会不依赖main函数的执行
    
            // 这样的方式,u2也不会依赖main函数的执行
            u2 := &User{"lily"}
            // m := func() {
            //         defer u2.Close()
            //         // u2 do somthing
            // }
            // m()
    func(){;u2.Close()}()     
    func() {
    defer u2.Close()
                     // u2 do somthing
            }()       
     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的功能特性,也满足范围精确控制的条件!

  • 相关阅读:
    iOS 关于字体根据不同屏幕尺寸等比适配的问题(zz)
    安卓开发:一种快速提取安卓app的UI图标资源的方法
    申请邓白氏编码的时候总是提示 Enter a valid Street Address 怎么办?
    利用日期、经纬度求日出日落时间 C语言程序代码(zz)
    JS导出Excel 代码笔记
    Bootstrap系列 -- 44. 分页导航
    Bootstrap系列 -- 43. 固定导航条
    Bootstrap系列 -- 42. 导航条中的按钮、文本和链接
    Bootstrap系列 -- 41. 带表单的导航条
    Bootstrap系列 -- 40. 导航条二级菜单
  • 原文地址:https://www.cnblogs.com/chuanheng/p/defer.html
Copyright © 2011-2022 走看看