golang中defer延遲機(jī)制的實(shí)現(xiàn)示例
defer是什么
defer是go中一種延遲調(diào)用機(jī)制。
執(zhí)行時(shí)機(jī)
defer后面的函數(shù)只有在當(dāng)前函數(shù)執(zhí)行完畢后才能執(zhí)行。
執(zhí)行順序
將延遲的語句按defer的逆序進(jìn)行執(zhí)行,也就是說先被defer的語句最后被執(zhí)行,最后被defer的語句,最先被執(zhí)行,通常用于釋放資源。
多個(gè)defer本質(zhì)就是用棧存儲(chǔ),先進(jìn)后出。
defer定義
//最后不要忘記函數(shù)調(diào)用
//使用匿名函數(shù)
defer func (形參列表){
}(實(shí)參)
//最后不要忘記調(diào)用函數(shù)
//使用事先聲明的函數(shù)
defer 函數(shù)名(實(shí)參)
//最后不要忘記調(diào)用方法
defer 方法名(形參列表){
}(實(shí)參)
defer的功能一般是用于釋放資源。
defer后面的函數(shù)是可以有返回值的,但是一般沒有作用。
注意事項(xiàng)
方法或函數(shù)必需調(diào)用
//報(bào)錯(cuò):未調(diào)用函數(shù)
defer func(){
fmt.Println("a")
}
注意聲明順序
雖然defer的執(zhí)行時(shí)機(jī)在函數(shù)結(jié)束后,但是聲明的時(shí)候使用的變量或者參數(shù)得是函數(shù)內(nèi)在defer聲明之前就定義好的。
//報(bào)錯(cuò):student未定義
defer student.GetName(2)
var student Student
//報(bào)錯(cuò):age未定義
defer func(a int){
fmt.Printf("年齡為%d\n",age)
}(age)
age := 15
多個(gè)defer的執(zhí)行順序
多個(gè)defer出現(xiàn)的時(shí)候,它會(huì)把defer之后的函數(shù)壓入一個(gè)棧中延遲執(zhí)行,也就是先進(jìn)后出(LIFO).
寫在前面的defer會(huì)比寫在后面的defer調(diào)用的晚。下面通過一個(gè)示例看一下:
package defer_knowledge
import "fmt"
type Student struct{
Name string
}
func (s Student) GetName(n int){
fmt.Printf("這是第%d個(gè)defer\n",n)
}
func sayHello(n int){
fmt.Printf("這是第%d個(gè)defer\n",n)
}
//驗(yàn)證defer的執(zhí)行順序
func DeferFirst(){
fmt.Println("hello world")
var age int = 25
defer func (){
fmt.Println("我是第1個(gè)defer")
}()
age++
/*
雖然defer的執(zhí)行時(shí)機(jī)在return之后
但是聲明defer時(shí),結(jié)構(gòu)體實(shí)例要先聲明,否則無法訪問結(jié)構(gòu)體實(shí)例方法
*/
var student Student
defer student.GetName(2)
defer func (){
fmt.Println("我是第3個(gè)defer")
}()
defer sayHello(4)
}
結(jié)果
這是第4個(gè)defer
我是第3個(gè)defer
這是第2個(gè)defer
我是第1個(gè)defer
圖示

延遲參數(shù)傳入時(shí)機(jī)
基本語法
注意事項(xiàng)
defer函數(shù)的入?yún)?shù)是在defer函數(shù)聲明時(shí)決定的。例如
package defer_knowledge
import "fmt"
//defer的參數(shù)是聲明時(shí)傳入的
func DeferParams(){
var age = 10
defer func(a int){
fmt.Printf("defer內(nèi)的參數(shù)為%d\n",a)
}(age)
age = 25
fmt.Printf("age已經(jīng)變成了%d\n",age)
}
調(diào)用結(jié)果
age已經(jīng)變成了25 defer內(nèi)的參數(shù)為10
小結(jié)
值類型
所以我們要注意傳入的參數(shù),
【值類型參數(shù)】
值類型參數(shù)原始變量改變不影響傳入?yún)?shù),例如int、數(shù)組、結(jié)構(gòu)體
如果我們想要defer執(zhí)行時(shí)能讀取到變化后的"值類型"參數(shù),可以傳入指針
例如
package defer_knowledge
import "fmt"
//defer的參數(shù)是聲明時(shí)傳入的
func DeferParams(){
var age = 10
//如果想要追蹤值類型的變化可以傳入值類型指針
defer func(a *int){
fmt.Printf("最初如果傳入指針,defer內(nèi)參數(shù)為%d\n",*a)
}(&age)
defer func(a int){
fmt.Printf("defer內(nèi)的參數(shù)為%d\n",a)
}(age)
age = 25
fmt.Printf("age已經(jīng)變成了%d\n",age)
}
調(diào)用結(jié)果
age已經(jīng)變成了25
defer內(nèi)的參數(shù)為10
最初如果傳入指針,defer內(nèi)參數(shù)為25
引用類型
具體看引用的底層是否發(fā)生變換,例如切片,如果沒發(fā)生擴(kuò)容將使用相同的。
package defer_knowledge
import "fmt"
func DeferParams2(){
var arr = make([]int,5,5)
//引用類型直接傳遞即可,將追蹤到引用改變?yōu)橹?
defer func(a []int){
fmt.Printf("defer內(nèi)的參數(shù)為%#v\n",a)
}(arr)
arr[2] = 10
fmt.Printf("arr已經(jīng)變成了%#v\n",arr)
}
調(diào)用結(jié)果
arr已經(jīng)變成了[]int{0, 0, 10, 0, 0}
defer內(nèi)的參數(shù)為[]int{0, 0, 10, 0, 0}
聲明時(shí)機(jī)和執(zhí)行時(shí)機(jī)
聲明時(shí)機(jī)
defer的聲明時(shí)機(jī)時(shí)按照他出現(xiàn)在代碼中的順序,這時(shí)會(huì)執(zhí)行兩個(gè)操作。
1.傳入?yún)?shù)
2.檢查內(nèi)部要訪問的變量是否已經(jīng)定義
舉例
func DeferTime(){
var age int
/*
聲明時(shí)會(huì)傳入?yún)?shù),以及檢查內(nèi)部邏輯是否正確
*/
defer func(){
//注意,這個(gè)不是defer函數(shù)的參數(shù)
//和常規(guī)變量作用域一樣,本層找不到就去外面找
age++
}()
}
錯(cuò)誤示范
func DeferTime(){
defer func(){
//報(bào)錯(cuò):age未定義
age++
}()
var age int
}
執(zhí)行時(shí)機(jī)
defer的執(zhí)行時(shí)機(jī)是在函數(shù)邏輯結(jié)束后,或者說return后,按照defer棧調(diào)用。
舉例
func DeferTime2(){
var age int
defer func(){
//按照defer棧,此時(shí)訪問到的age為 11
age = age+5
fmt.Printf("age的值為%d\n",age)
}()
defer func(){
//defer執(zhí)行時(shí)機(jī)為函數(shù)結(jié)束后,所以此時(shí)訪問到的 age = 10
age++
fmt.Printf("age的值為%d\n",age)
}()
age = 10
}
結(jié)果
age的值為11
age的值為16
defer與return的區(qū)別
圖示

可以看到 return 執(zhí)行的時(shí)候,并不是原子性操作,一般是分為兩步:將結(jié)果x賦值給了返回值,然后執(zhí)行了RET指令;而defer語句執(zhí)行的時(shí)候,是在賦值變量之后,在RET指令之前。所以這里注意一下。返回值和x的關(guān)系。如果x是一個(gè)值類型,這里是進(jìn)行了拷貝的。
執(zhí)行圖示意

函數(shù)返回值
不具名返回
形式
func 函數(shù)名(參數(shù)列表) 返回值類型{
return 返回值
}
//例如
/*
return sum
操作拆解:
實(shí)際對(duì)外暴露返回值為 sum_copy
sum_copy = sum
所以return實(shí)際執(zhí)行拷貝操作,他不是將函數(shù)內(nèi)的變量拋出,而是將拷貝后的值拋出
*/
func Add(a,b int) int{
sum := a+b
return sum
}
案例1
func DeferAndReturn1() int{
var num int
defer func(){
num++
//num的值為16
fmt.Printf("num的值為%d\n",num)
}()
num = 15
return num
}
//調(diào)用
target := DeferAndReturn1()
//target的值為15
fmt.Printf("target的值為%d\n",target)
解析
func DeferAndReturn1() int{
var num int
defer func(){
num++
//num的值為16
fmt.Printf("num的值為%d\n",num)
}()
num = 15
/*
實(shí)際操作
copy_num = num
對(duì)外暴露copy_num,
由于num是值類型,所以后續(xù)defer中對(duì)num的操作不影響copy_num
*/
return num
}
誤區(qū)1
想到了指針操作,但是理解出錯(cuò)。
func DeferAndReturn1() int{
var num int
var ptr = new(int)
ptr = &num
defer func(){
*ptr++
//num的值為16
fmt.Printf("num的值為%d\n",num)
}()
num = 15
return num
}
//調(diào)用
target := DeferAndReturn1()
//target的值為15
fmt.Printf("target的值為%d\n",target)
原因:
func DeferAndReturn1() int{
var num int
var ptr = new(int)
ptr = &num
defer func(){
*ptr++
//num的值為16
fmt.Printf("num的值為%d\n",num)
}
num = 15
/*
實(shí)際操作
copy_num = num
對(duì)外暴露copy_num,
我們修改通過指針ptr修改num的值,還是沒影響到copy_num
*/
return num
}
正確思維
func DeferAndReturn1() *int{
var num int
var ptr = new(int)
ptr = &num
defer func(){
num++
//num的值為16
fmt.Printf("num的值為%d\n",num)
}()
num = 15
return ptr
}
//調(diào)用
target := DeferAndReturn1()
//target的值為16
fmt.Printf("target的值為%d\n",*target)
結(jié)果
num的值為16
target的值為16
原因
func DeferAndReturn1() *int{
var num int
var ptr = new(int)
ptr = &num
defer func(){
num++
//num的值為16
fmt.Printf("num的值為%d\n",num)
}()
num = 15
/*
實(shí)際操作
copy_ptr = ptr
由于ptr是引用類型,所以defer對(duì)ptr的影響會(huì)影響到copy_ptr
num的本質(zhì)就是*ptr,操作num就是在操作ptr
*/
return ptr
}
具名返回
相當(dāng)于實(shí)際要暴露的返回值早就確定好了,return只是起到一個(gè)結(jié)束函數(shù)的作用。
func 函數(shù)名(參數(shù)列表)(返回值 返回值類型){
return
}
//示例
/*
sum就是實(shí)際暴露的返回值,且已經(jīng)聲明了
*/
func Add(a,b int) (sum int){
return
}
案例1
func DeferAndReturn2() (num int){
defer func(){
num++
}()
num = 10
return
}
num2 := DeferAndReturn2()
fmt.Printf("外部num的值為%d\n",num2)
結(jié)果
外部num的值為11
原因
func DeferAndReturn2() (num int){
defer func(){
num++
}()
num = 10
/*
這里寫return 和 return num一樣
最終暴露的值為 num
所以defer中對(duì)num的操作會(huì)影響到最終返回值
*/
return
}
到此這篇關(guān)于golang中defer延遲機(jī)制的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)golang defer延遲機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
GO接收GET/POST參數(shù)及發(fā)送GET/POST請(qǐng)求的實(shí)例詳解
這篇文章主要介紹了GO接收GET/POST參數(shù)及發(fā)送GET/POST請(qǐng)求,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12
golang遍歷處理map時(shí)的常見性能陷阱與解決方法
這篇文章主要為大家詳細(xì)介紹了Golang中有關(guān)循環(huán)處理map時(shí)的性能優(yōu)化,本文主要介紹了常見的三種場(chǎng)景,文中的示例代碼講解詳細(xì),需要的可以了解下2025-05-05
Go依賴注入DI工具wire使用詳解(golang常用庫包)
依賴注入是指程序運(yùn)行過程中,如果需要調(diào)用另一個(gè)對(duì)象協(xié)助時(shí),無須在代碼中創(chuàng)建被調(diào)用者,而是依賴于外部的注入,本文結(jié)合示例代碼給大家介紹Go依賴注入DI工具wire使用,感興趣的朋友一起看看吧2022-04-04
Golang使用WebSocket通信的實(shí)現(xiàn)
這篇文章主要介紹了Golang使用WebSocket通信的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02
Go語言結(jié)合Gin框架快速實(shí)現(xiàn)分頁查詢接口
在開發(fā)?Web?應(yīng)用時(shí),分頁查詢?是非常常見的需求,在?Go?語言中,我們可以結(jié)合?GORM?+?Gin?框架,快速實(shí)現(xiàn)分頁查詢接口,下面我們來看看具體實(shí)現(xiàn)方法吧2025-08-08
golang實(shí)現(xiàn)京東支付v2版本的示例代碼
這篇文章主要介紹了golang實(shí)現(xiàn)京東支付v2版本,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03

