Golang入門教程(十三)延遲函數defer詳解
前言
大家都知道go語言的defer功能很強大,對於資源管理非常方便,但是如果沒用好,也會有陷阱哦。Go 語言中延遲函數 defer 充當著 try...catch 的重任,使用起來也非常簡便,然而在實際應用中,很多 gopher 並沒有真正搞明白 defer、return、返回值、panic 之間的執行順序,從而掉進坑中,今天我們就來揭開它的神秘面紗!話不多說了,來一起看看詳細的介紹吧。
基本介紹
延時調用函數的語法如下:
defer func_name(param-list)
當一個函數調用前有關鍵字 defer 時, 那麽這個函數的執行會推遲到包含這個 defer 語句的函數即將返回前才執行. 例如:
package main
import (
"fmt"
)
func main() {
defer fmt.Println("我是最後執行的")
fmt.Println("我是第一個")
fmt.Println("我是第二個")
}
執行結果
小結:defer 調用的函數參數的值 defer 被定義時就確定了
案例2
package main import "fmt" func main() { i := 1 defer fmt.Println("Deferred print:",i) i++ fmt.Println("Normal print:", i) }
在 "defer fmt.Println("Deferred print:", i)" 調用時, i 的值已經確定了, 因此相當於 defer fmt.Println("Deferred print:", 1)了
小結:需要強調的時, defer 調用的函數參數的值在 defer 定義時就確定了, 而 defer 函數內部所使用的變量的值需要在這個函數運行時才確定
案例3
package main import "fmt" func f1() (r int) { r = 1 defer func() { r++ fmt.Println(" r value = ",r) }() r = 2 return } func main() { f1() }
小結:上面的例子中, 最終打印的內容是 "3", 這是因為在 "r = 2" 賦值之後, 執行了 defer 函數, 因此在這個函數內, r 的值是2了, 自增後變為3
函數定義
func function_name( [parameter list] ) [return_types] {
函數體
}
或者
func funcName(形參1 type[, 形參2 type...]) [([[返回變量1] type[, [返回變量2] type...]])] {
[return [返回變量1[, 返回變量2...]]]
}
defer 順序
如果有多個defer 調用, 則調用的順序是先進後出的順序, 類似於入棧出棧一樣(後進先出/先進後出)
案例4
package main
import "fmt"
func f3() (r int) {
defer func() {
r++
}()
return 0
}
func main() {
fmt.Println(f3())
}
上面 fmt.Println(f1()) 打印的是什麽呢? 很多朋友可能會認為打印的是0, 但是正確答案是 1. 這是為什麽呢?
要弄明白這個問題, 我們需要牢記兩點:
- defer 函數調用的執行時機是外層函數設置返回值之後, 並且在即將返回之前
- return XXX 操作並不是原子的
匿名返回值
案例5
package main
import "fmt"
func main() {
fmt.Println("a return:", a()) // 打印結果為 a return: 0
}
func a() int {
var i int
defer func() {
i++
fmt.Println("a defer2:", i) // 打印結果為 a defer2: 2
}()
defer func() {
i++
fmt.Println("a defer1:", i) // 打印結果為 a defer1: 1
}()
return i
}
參考
1、golang defer 使用小結與註意要點
2、Go語言中的延遲函數defer示例詳解
Golang入門教程(十三)延遲函數defer詳解