• go 学习笔记 ----资源自动回收


    在释放局部资源时, 可以用defer管理

    Go语言版本基于deferMutex用法

    func safeRead(Mutex *mu) []byte {
    	mu.Lock()
    	defer mu.Unlock()
    	return read();
    }
    

      

    对于可能申请失败的资源也很好处理:

    func loadFile(name string) ([]byte, error) {
    	f, err := os.Open(name)
    	if err != nil {
    		return nil, err
    	}
    	defer f.Close()
    	return load(f)
    }
    

    使用defer语句, 可以方便地组合函数/闭包和资源对象. 即使panic时, defer也能保证资源的正确释放.

    defer

    官方给出的文档上介绍defer的执行有三条基本规则:

    1. defer函数是在外部函数return后,按照后申明先执行(栈)的顺序执行的;
    package main
    
    import "fmt"
    
    func main() {
        defer fmt.Println("1")
        defer fmt.Println("2")
        defer fmt.Println("3")
    }
    

      

    输出是

    3
    2
    1
    2. defer函数的参数的值,是在申明defer时确定下来的;

    看几个例子吧,注意第一条规则。
    这是一个普通类型的例子:

    package main
    
    import "fmt"
    
    func main() {
        i := 0
    
        defer fmt.Println(i)  // 这也算是作为defer函数的参数
        defer func(j int) { fmt.Println(j) }(i)  // 作为参数
        defer func() { fmt.Println(i) }()  // 作为闭包(closure)进行引用
    
        i++
    }
    

      

    输出是:

    1
    0
    0

    如果是引用类型也是一样的道理:

    • 当修改引用对象的属性时:
    • package main
      
      import "fmt"
      
      type Person struct {
          name string
      }
      
      func main() {
          person := &Person{"Lilei"}
      
          defer fmt.Println(person.name)  // person.name作为普通类型当做defer函数的参数
          defer fmt.Printf("%v
      ", person)  // 引用类型作为参数
          defer func(p *Person) { fmt.Println(p.name) }(person)  // 同上
          defer func() { fmt.Println(person.name) }()  // 闭包引用,对引用对象属性的修改不影响引用
      
          person.name = "HanMeimei"
      }
      

        

      输出是:

      HanMeimei
      HanMeimei
      &{HanMeimei}
      Lilei
    • 当修改引用本身时:
    package main
    
    import "fmt"
    
    type Person struct {
        name string
    }
    
    func main() {
        person := &Person{"Lilei"}
    
        defer fmt.Println(person.name)  // 同上,person.name作为普通类型当做defer函数的参数
        defer fmt.Printf("%v
    ", person)  // 作为defer函数的参数,申明时指向“Lilei”
        defer func(p *Person) { fmt.Println(p.name) }(person)  // 同上
        defer func() { fmt.Println(person.name) }()  // 作为闭包引用,随着person的改变而指向“HanMeimei”
    
        person = &Person{"HanMeimei"}
    }
    

      

    输出是:

    HanMeimei
    Lilei
    &{Lilei}
    Lilei

    在defer函数申明时,对外部变量的引用是有两种方式的,分别是作为函数参数和作为闭包引用。作为函数参数,则在defer申明时就把值传递给defer,并被cache起来。作为闭包引用的话,则会在defer函数执行时根据整个上下文确定当前的值。
    3. defer函数可以读取和修改外部函数申明的返回值。

    同样给一个例子

    package main
    
    import "fmt"
    
    func main() {
        fmt.Printf("output: %d
    ", f())
    }
    
    func f() (i int) {
        defer fmt.Println(i)  // 参数引用
        defer func(j int) { fmt.Println(j) }(i)  // 同上
        defer func() { fmt.Println(i) }()  // 闭包引用
        defer func() { i++ }()  // 执行前,i=2
        defer func() { i++ }()  // 执行前,i=1
    
        i++
    
        return
    }
    

      

    有了之前的基础,这个输出可以轻松推测出来:

    3
    0
    0
    output: 3


    defer的基本规则就这三条了。了解之后,瞬间明朗了。

    最开始的答案

    通过defer的三条规则(其实只要前两条)就能知道输出了:

    3
    3
    3
    0
     
     

     

  • 相关阅读:
    一步一步制作jquery插件Tabs(ajax只请求一次效果,78行完成)
    javascript没那么简单
    jQuery插件制作备忘
    封装jQuery表格插件jqGrid,控件化jqGrid(二):显示
    shell脚本day01bash的基本特性
    pythonday01_语言元素
    pythonday01_环境搭建
    shell脚本作业
    [转]ADS简要教程
    极限挑战—C#+ODP 100万条数据导入Oracle数据库仅用不到1秒
  • 原文地址:https://www.cnblogs.com/saryli/p/11371709.html
Copyright © 2020-2023  润新知