概述
对于资源释放,有不少不一样的实现方式,不一样语言也有不一样的惯用方法。
- 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 语句中的函数调用是当前函数最后执行的东西`
- 一个经典的例子
1 // Contents returns the file's contents as a string.
2 func Contents(filename string) (string, error) {
3 f, err := os.Open(filename)
4 if err != nil {
5 return "", err
6 }
7 defer f.Close() // f.Close will run when we're finished.
8
9 var result []byte
10 buf := make([]byte, 100)
11 for {
12 n, err := f.Read(buf[0:])
13 result = append(result, buf[0:n]...) // append is discussed later.
14 if err != nil {
15 if err == io.EOF {
16 break
17 }
18 return "", err // f will be closed if we return here.
19 }
20 }
21 return string(result), nil // f will be closed if we return here.
22 }
用上面的话翻译代码中的`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语句的时候,究竟是谁的函数调用先执行呢?
1 for i := 0; i < 5; i++ {
2 defer fmt.Printf("%d ", i)
3 }
defer的函数调用按着后进先出(LIFO)的方式执行。大概猜一猜,每次defer都会将函数调用压入栈中,最后依次出栈执行。程序员
最后
golang中defer也就主要用在资源管理上了。明确以上几点问题,应该问题不大了(吹牛ing)。