概述
對於資源釋放,有不少不一樣的實現方式,不一樣語言也有不一樣的慣用方法。
- 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)。