預習內容
- defer 的作用有哪些?
- 多個 defer 的執行順序是怎樣的?
- defer,return,函式返回值 三者之間的執行順序
defer的作用
go中的defer
是延遲函式,一般是用於釋放資源或者收尾工作。
由於defer
是具有延遲特性且執行動作是在函式return之後,因此作為資源釋放作用再好不過。
- 典型例子:釋放鎖、關閉檔案、關閉連結等
// 釋放鎖
func getValue() {
s.Lock()
defer s.Unlock()
...
}
// 關閉檔案
func read() {
f, err := os.OpenFile("filename", os.O_RDWR, os.ModePerm)
defer f.Close()
...
}
// 關閉連結
func connect() {
resp, err := grequests.Get(s.cfg.GateApiCrossMarginUrl, nil)
defer resp.Close()
...
}
// 收尾工作,defer 同時也是函式,可以做很多收尾相關工作
func closeConnection() {
...
defer func() {
file.close()
close(readChan)
}
...
}
- 還有作用就是捕獲 panic,這個功能在defer裡也是典型用法
func sendChan() {
// 此處捕獲 panic 進行 recover 防止程式崩潰
defer func() {
if ok := recover(); ok != nil {
fmt.Println("recover")
}
}()
// 向已經關閉的chan傳送資料,此處會引起 panic
dataChan <- "message"
...
}
defer 釋放資源『避坑指南』
資源釋放動作一定緊跟資源使用(開啟、連線)語句,不然defer可能不會被執行到,導致記憶體洩露
// 關閉檔案
func read() error {
f1, err := os.OpenFile("filename", os.O_RDWR, os.ModePerm)
if err != nil {
return err
}
// 此時,defer還沒執行到,提前return了,無效defer導致記憶體洩露
defer f1.Close()
// 正確用法,緊跟資源使用語句
f2, err := os.OpenFile("filename", os.O_RDWR, os.ModePerm)
defer f2.Close()
if err != nil {
return err
}
}
defer 的呼叫順序
牆裂建議不要先看後面的介紹做下面的題目,此處先跳過介紹defer呼叫順序,看看下面比較典型的對defer順序判斷,你能看出來幾個?
func deferFunc1(i int) (t int) {
t = i
defer func() {
t += 1
}()
return t
}
func deferFunc2(i int) int {
t := i
defer func() {
t += 1
}()
return t
}
func deferFunc3(i int) (t int) {
defer func() {
t += i
}()
return 1
}
func deferFunc4() (t int) {
defer func(i int) {
fmt.Println(i)
fmt.Println(t)
}(t)
t = 0
return 1
}
func ExecDeferFunc() {
// 猜猜下面輸出的內容和順序
fmt.Println(deferFunc1(1))
fmt.Println(deferFunc2(1))
fmt.Println(deferFunc3(1))
deferFunc4()
}
猜想結果可能是:2,2,1,0,0 或者 2,2,1,1,1 ?
估計比較模糊的地方應該是函式返回值 和 return value(函式返回值)關係不明確,還有就是對defer產生作用的時機不明確
正文開始!
再次強調defer
是延遲函式,執行動作在return之後,defer相當於是將執行動作壓入棧中,越是後面的defer越是先執行,執行順序是LIFO(後進先出)。
特別指出:有函式返回值的則return將結果寫入返回值,defer進行收尾,可以看做 return最先執行,然後return將結果存入返回值,最後defer執行
那麼基於剛剛的介紹再回頭去看得到的『實際結果是:2,1,2,0,1』
複習內容
- defer 用於資源釋放和收尾工作
- 多個 defer 呼叫順序是 LIFO(後入先出),defer後的操作可以理解為壓入棧中
- defer,return,return value(函式返回值) 執行順序:首先return,其次return value,最後defer。defer可以修改函式最終返回值。