欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

golang中defer延遲機(jī)制的實(shí)現(xiàn)示例

 更新時(shí)間:2025年09月29日 09:20:53   作者:lmryBC49  
本文講解了golang特殊的defer延遲機(jī)制,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

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語言三種變量作用域的實(shí)現(xiàn)

    Go語言三種變量作用域的實(shí)現(xiàn)

    本文介紹了Go語言中變量的三種作用域:全局作用域、包作用域和函數(shù)作用域,并詳細(xì)解釋了作用域規(guī)則,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-09-09
  • GO接收GET/POST參數(shù)及發(fā)送GET/POST請(qǐng)求的實(shí)例詳解

    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í)的常見性能陷阱與解決方法

    golang遍歷處理map時(shí)的常見性能陷阱與解決方法

    這篇文章主要為大家詳細(xì)介紹了Golang中有關(guān)循環(huán)處理map時(shí)的性能優(yōu)化,本文主要介紹了常見的三種場(chǎng)景,文中的示例代碼講解詳細(xì),需要的可以了解下
    2025-05-05
  • Go依賴注入DI工具wire使用詳解(golang常用庫包)

    Go依賴注入DI工具wire使用詳解(golang常用庫包)

    依賴注入是指程序運(yùn)行過程中,如果需要調(diào)用另一個(gè)對(duì)象協(xié)助時(shí),無須在代碼中創(chuàng)建被調(diào)用者,而是依賴于外部的注入,本文結(jié)合示例代碼給大家介紹Go依賴注入DI工具wire使用,感興趣的朋友一起看看吧
    2022-04-04
  • 使用Go語言實(shí)現(xiàn)心跳機(jī)制

    使用Go語言實(shí)現(xiàn)心跳機(jī)制

    心跳最典型的應(yīng)用場(chǎng)景是是探測(cè)服務(wù)是否存活,這篇文章主要來和大家介紹一下如何使用Go語言實(shí)現(xiàn)一個(gè)簡(jiǎn)單的心跳程序,感興趣的可以了解下
    2024-01-01
  • Go語言中defer使用的陷阱小結(jié)

    Go語言中defer使用的陷阱小結(jié)

    本文主要介紹了Go語言中defer使用的陷阱小結(jié),分別是defer語句不可以在return語句之后,defer語句執(zhí)行的匿名函數(shù),匿名函數(shù)的參數(shù)會(huì)被預(yù)先處理,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • Golang使用WebSocket通信的實(shí)現(xiàn)

    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)分頁查詢接口

    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版本的示例代碼

    這篇文章主要介紹了golang實(shí)現(xiàn)京東支付v2版本,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • 一文帶你熟悉Go語言中函數(shù)的使用

    一文帶你熟悉Go語言中函數(shù)的使用

    這篇文章主要和大家分享一下Go語言中的函數(shù)的使用,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Go語言有一定的幫助,需要的小伙伴可以參考一下
    2022-11-11

最新評(píng)論