一文搞懂Go語言中defer關鍵字的使用
前言
defer是golang中用的比較多的一個關鍵字,也是go面試題里經(jīng)常出現(xiàn)的問題,而在很多時候我們只知其然,而不知其所以然,今天就來整理一下關于defer的學習使用,希望對需要的朋友有所幫助。
defer是什么
defer是go中一種延遲調(diào)用機制,defer后面的函數(shù)只有在當前函數(shù)執(zhí)行完畢后才能執(zhí)行,將延遲的語句按defer的逆序進行執(zhí)行,也就是說先被defer的語句最后被執(zhí)行,最后被defer的語句,最先被執(zhí)行,通常用于釋放資源。
定義:
defer function([parameter_list]) // 延遲執(zhí)行函數(shù) defer method([parameter_list]) // 延遲執(zhí)行方法
多個defer的執(zhí)行順序
多個defer出現(xiàn)的時候,它會把defer之后的函數(shù)壓入一個棧中延遲執(zhí)行,也就是先進后出(LIFO),寫在前面的defer會比寫在后面的defer調(diào)用的晚。下面通過一個示例看一下:
func func1(){
fmt.Println("我是 func1")
}
func func2(){
fmt.Println("我是 func2")
}
func func3(){
fmt.Println("我是 func3")
}
func main(){
defer func1()
defer func2()
defer func3()
fmt.Println("main1")
fmt.Println("main2")
}執(zhí)行輸出如下:
main1
main2
我是 func3
我是 func2
我是 func1
通過圖示一看就很明白了

延遲函數(shù)的參數(shù)在defer聲明時就決定了
func main(){
i:= 0
defer func(a int) {
fmt.Println(a)
}(i)
i++
}此時輸出的值是0,而不是1,因為defer后面的函數(shù)在入棧的時候保存的是入棧那一刻的值,而當時i的值是0,所以后期對i進行修改,并不會影響棧內(nèi)函數(shù)的值。
如果我們把參數(shù)傳引用
func main(){
i:= 0
defer func(a *int) {
fmt.Println(*a)
}(&i)
i++
}此時輸出的值是1,因為這里defer后面函數(shù)入棧的時候唇乳的執(zhí)行變量i的指針,后期i值改變的時候,輸出結果也會改變。
defer和return的順序
首先看下defer和return語句的區(qū)別,如下:

可以看到 return 執(zhí)行的時候,并不是原子性操作,一般是分為兩步:將結果x賦值給了返回值,然后執(zhí)行了RET指令;而defer語句執(zhí)行的時候,是在賦值變量之后,在RET指令之前。所以這里注意一下。返回值和x的關系。如果x是一個值類型,這里是進行了拷貝的。
示例:
package main
import "fmt"
func deferFunc() int {
fmt.Println("defer func called")
return 0
}
func returnFunc() int {
fmt.Println("return func called")
return 0
}
func returnAndDefer() int {
defer deferFunc()
return returnFunc()
}
func main() {
returnAndDefer()
}
執(zhí)行結果為:
return func called
defer func called
defer和panic
當函數(shù)遇到panic,defer仍然會被執(zhí)行。Go會先執(zhí)行所有的defer鏈表(該函數(shù)的所有defer),當所有defer被執(zhí)行完畢且沒有recover時,才會進行panic。
defer 最大的功能是 panic 后依然有效,所以defer可以保證你的一些資源一定會被關閉,從而避免一些異常出現(xiàn)的問題。
package main
import "fmt"
func main() {
deferPanic()
}
func deferPanic() {
defer fmt.Println("defer 1")
defer fmt.Println("defer 2")
defer fmt.Println("defer 3")
panic("出錯啦")
}執(zhí)行輸出如下:
defer 3
defer 2
defer 1
panic: 出錯啦
我們可以在defer中進行recover,如果defer中包含recover,則程序?qū)⒉粫龠M行panic,這就實現(xiàn)了Go中異常拋出/捕獲類似的機制。
package main
import (
"fmt"
)
func main() {
defer func() {
//捕獲異常
if err := recover(); err != nil{
fmt.Println(err)
}else {
fmt.Println("fatal")
}
}()
//拋出異常
panic("panic")
}defer下的函數(shù)參數(shù)包含子函數(shù)
package main
import "fmt"
func function(index int, value int) int {
fmt.Println(index)
return index
}
func main() {
defer function(1, function(3, 0))
defer function(2, function(4, 0))
這個程序的執(zhí)行結果是怎么樣的的?
首先兩個defer會壓棧兩次,先進棧1,后進棧2,在壓棧function1的時候,需要連同函數(shù)地址、函數(shù)形參一同進棧,那么為了得到function1的第二個參數(shù)的結果,需要先執(zhí)行function3將第二個參數(shù)算出,所以function3就被第一個執(zhí)行。同理壓入棧function2,就需要先執(zhí)行function4算出function2的第二個參數(shù)的值,然后函數(shù)結束,先出棧function2、再出棧function1。輸出結果如下:
3
4
2
1
總結
- defer是go中一種延遲調(diào)用機制,defer后面的函數(shù)只有在當前函數(shù)執(zhí)行完畢后才能執(zhí)行。
- 多個defer出現(xiàn)的時候,它會把defer之后的函數(shù)壓入一個棧中延遲執(zhí)行,也就是先進后出。
- defer后面的函數(shù)值在入棧的時候就決定了。
- defer 最大的功能是 panic 后依然有效,我們可以在defer中進行recover,如果defer中包含recover,則程序?qū)⒉粫龠M行panic,實現(xiàn)try catch機制。
到此這篇關于一文搞懂Go語言中defer關鍵字的使用的文章就介紹到這了,更多相關Go語言 defer內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Go語言并發(fā)編程之互斥鎖Mutex和讀寫鎖RWMutex
Go 語言中提供了很多同步工具,本文將介紹互斥鎖Mutex和讀寫鎖RWMutex的使用方法,想要具體了解的小伙伴,請參考下面文章詳細內(nèi)容,希望對你有所幫助2021-10-10
Go結構體SliceHeader及StringHeader作用詳解
這篇文章主要為大家介紹了Go結構體SliceHeader及StringHeader作用的功能及面試官愛問的實際意義詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-07-07

