zoukankan      html  css  js  c++  java
  • 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
     
     

     

  • 相关阅读:
    protobuf(Protocol Buffers)java初体验
    排序(6)---------归并排序(C语言实现)
    JSP/Servlet-----charset 、pageEncoding差别
    [Android] Android开发优化之——对界面UI的优化(2)
    [Android] Android开发优化之——对界面UI的优化(1)
    [Android] Android开发优化之——从代码角度进行优化
    Android开发优化之——对Bitmap的内存优化
    Java 如何有效地避免OOM:善于利用软引用和弱引用
    开源中国源码学习(八)——枚举类
    Android ADB server didn't ACK * failed to start daemon * 简单有效的解决方案
  • 原文地址:https://www.cnblogs.com/saryli/p/11371709.html
Copyright © 2011-2022 走看看