golang之defer

## 概述 对于资源释放,有很多不同的实现方式,不同语言也有不同的惯用方法。 - C语言 :手动管理 - Golang :defer - Python :上下文管理器contexManager - C++ : 作用域和析构函数 - Rust :所有权和drop trait   如果了解上面几种语言的童鞋应该知道, C语言资源管理是比较麻烦的,一旦资源使用过程中出错,就可能造成资源泄漏。 Golang通过defer,即使过程中panic,也可以释放资源。 Python通过上下文管理器,主要是两个magic function`__enter__`, `__exit__`来保证资源的释放。 C++和Rust相似,都是在某种语义下自动调用释放函数。但是Rust有所有权检查,可以防止写代码犯傻 (比如C++不小心拷贝了一下。)     以上来看,C++和Rust的必须要在编程时注意释放的时机,也就是需要程序员更多的思考。但是Rust编译器会帮你一下,而C++并不会。 其次Python和Golang都使用了显式管理,一定不能忘了做,不过做了问题就不大了。   ## defer 不过这是一篇关于derfer的文章,写一下defer需要注意的重点(就是读Effective Go的一点笔记)。   ### 基本使用 `defer语句将函数调用安排在当前函数结束前执行。也就是defer 语句中的函数调用是当前函数最后执行的东西` - 一个经典的例子 ```
// Contents returns the file's contents as a string.
func Contents(filename string) (string, error) {
    f, err := os.Open(filename)
    if err != nil {
        return "", err
    }
    defer f.Close()  // f.Close will run when we're finished.

    var result []byte
    buf := make([]byte, 100)
    for {
        n, err := f.Read(buf[0:])
        result = append(result, buf[0:n]...) // append is discussed later.
        if err != nil {
            if err == io.EOF {
                break
            }
            return "", err  // f will be closed if we return here.
        }
    }
    return string(result), nil // f will be closed if we return here.
}
``` 用上面的话翻译代码中的`defer f.Close()`就是“将f.Close()放在Contents函数的最后执行” 将`f.Close()`放在defer后面有两个好处:保证资源释放、离`Open`比较近不会忘了做。 这没什么好说的,golang必须显式释放资源。   ### 细节 首先明确两个概念 - defer语句执行     将defer语句中函数调用安排在了当前函数结束前执行 - 函数调用执行     运行defer语句中的函数调用   #### 参数求值 这里的参数求值指的是defer语句中函数调用的参数。 参数在defer语句执行求值,而不是在函数调用执行时求值。 - 又一个例子 ```
func trace(s string) string {
    fmt.Println("entering:", s)
    return s
}

func un(s string) {
    fmt.Println("leaving:", s)
}

func a() {
    defer un(trace("a"))
    fmt.Println("in a")
}

func b() {
    defer un(trace("b"))
    fmt.Println("in b")
    a()
}

func main() {
    b()
}
``` 首先看函数b,因为参数是在defer语句执行时求值的,所以`trace("b")`要先被求值[先打印"b",再返回"b"],然后再往下执行,在函数b结束之前会调用`un("b")`. 同理如函数a。现在猜一下执行结果。 ```
entering: b
in b
entering: a
in a
leaving: a
leaving: b
``` #### 执行顺序 当有多个defer语句的时候,到底是谁的函数调用先执行呢? ```
for i := 0; i < 5; i++ {
    defer fmt.Printf("%d ", i)
}
``` defer的函数调用按着后进先出(LIFO)的方式执行。大概猜一猜,每次defer都会将函数调用压入栈中,最后依次出栈执行。   ## 最后 golang中defer也就主要用在资源管理上了。明确以上几点问题,应该问题不大了(吹牛ing)。

   
上一篇:Go每日一题(1)


下一篇:Golang的FAQ