溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

golang中的defer函數(shù)怎么用

發(fā)布時(shí)間:2022-10-26 09:24:17 來(lái)源:億速云 閱讀:77 作者:iii 欄目:開(kāi)發(fā)技術(shù)

本文小編為大家詳細(xì)介紹“golang中的defer函數(shù)怎么用”,內(nèi)容詳細(xì),步驟清晰,細(xì)節(jié)處理妥當(dāng),希望這篇“golang中的defer函數(shù)怎么用”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來(lái)學(xué)習(xí)新知識(shí)吧。

    golang的defer

    什么是defer

    go語(yǔ)言中defer可以完成延遲功能,當(dāng)前函數(shù)執(zhí)行完成后再執(zhí)行defer的代碼塊。通過(guò)defer,我們可以在代碼中優(yōu)雅的關(guān)閉/清理代碼中所使用的變量。

    defer是Go語(yǔ)言中的延遲執(zhí)行語(yǔ)句,用來(lái)添加函數(shù)結(jié)束時(shí)執(zhí)行的代碼,常用于釋放某些已分配的資源、關(guān)閉數(shù)據(jù)庫(kù)連接、斷開(kāi)socket連接、解鎖一個(gè)加鎖的資源。

    Go語(yǔ)言機(jī)制擔(dān)保一定會(huì)執(zhí)行defer語(yǔ)句中的代碼。其它語(yǔ)言中也有類似的機(jī)制,比如Java、C#語(yǔ)言里的finally語(yǔ)句,C++語(yǔ)言里的析構(gòu)函數(shù)(Destructor)可以起類似的作用,C++語(yǔ)言機(jī)制擔(dān)保在對(duì)象被銷毀前一定會(huì)執(zhí)行析構(gòu)函數(shù)中的代碼。C++中的析構(gòu)函數(shù)析構(gòu)的是對(duì)象,Go中的defer析構(gòu)的是函數(shù)。

    理解defer

    defer什么時(shí)間執(zhí)行(defer、 return、返回值 三者的執(zhí)行順序)

    defer只有在當(dāng)前函數(shù)執(zhí)行完畢后,才會(huì)執(zhí)行。描述其實(shí)不太精確

    go中的return語(yǔ)句并不是原子性操作,一般是分為兩步:

    • 將返回值賦值給一個(gè)變量

    • 執(zhí)行RET指令

    return并不是原子性操作,是通過(guò)一個(gè)變量賦值和ret指令來(lái)完成的。defer就執(zhí)行在1之后,2之前。defer的執(zhí)行順序在return之后,但是在返回值返回給調(diào)用方之前,所以使用defer可以達(dá)到修改返回值的目的。

    defer、 return、返回值 三者的執(zhí)行順序是 : return 最先給返回值賦值;接著 defer 開(kāi)始執(zhí)行一些收尾工作;最后 RET 指令攜帶返回值退出函數(shù)。

    package main
    
    import (
        "fmt"
    )
    
    func main() {
        ret := test()
        fmt.Println("test return:", ret)
    }
    // func test() ( int) {  這種就是匿名返回值
    //返回值改為命名返回值, 具名返回值。即返回值帶有名字, 這樣我們?cè)趫?zhí)行defer的時(shí)候相當(dāng)于修改了返回值的值
    func test() (i int) {
        //var i int
    
        defer func() {
            i++
            fmt.Println("test defer, i = ", i)
        }()
    
        return i
    }

    注意: 這塊驗(yàn)證使用了具名返回值 func test() (i int) { 中的(i int) 。 測(cè)試結(jié)果滿足我們預(yù)期。

    編碼中,我們要特別注意, go語(yǔ)言中匿名返回值和命名返回值對(duì)defer的影響。不過(guò)一般我們都是使用命名返回值。

    一個(gè)主函數(shù)擁有一個(gè)匿名的返回值,返回時(shí)使用字面值,比如返回”1”、”2”、”Hello”這樣的值,這種情況下defer語(yǔ)句是無(wú)法操作返回值的。

    defer輸出的值,就是定義時(shí)的值。而不是defer真正執(zhí)行時(shí)的變量值(注意引用情況)

    defer函數(shù)會(huì)在return之后被調(diào)用。那么這段函數(shù)執(zhí)行完之后,是不用應(yīng)該輸出1呢?

    package main
    
    import "fmt"
    
    func test1() {
    	i := 0
    	defer fmt.Println(i)
    	i++
    	return
    }
    
    func main() {
    	test1()
    }

    輸出結(jié)果:0

    雖然我們?cè)赿efer后面定義的是一個(gè)帶變量的函數(shù): fmt.Println(i). 但這個(gè)變量(i)在defer被聲明的時(shí)候,就已經(jīng)確定其確定的值了。

    總結(jié): 因?yàn)閐efer后面的函數(shù)在入棧的時(shí)候保存的是入棧那一刻的值,而當(dāng)時(shí)i的值是0,所以后期對(duì)i修改,并不會(huì)影響棧內(nèi)函數(shù)的值。

    我們?cè)倏聪乱粋€(gè)例子:

    package main
    
    import "fmt"
    
    
    func test2() {
    	x := 10
    	defer func(a *int) {
    		fmt.Println(*a)
    	}(&x)
    	x++
    }
    
    func main() {
    	test2()
    }

    輸出結(jié)果: 11

    這里為什么和前面結(jié)論不一樣呢?
    這里defer后面函數(shù)入棧的時(shí)候存入的執(zhí)行變量x的指針。所以,后期x值改變的時(shí)候,輸出結(jié)果也會(huì)改變。

    總結(jié): 需要注意引用情況。對(duì)于指針類型參數(shù),規(guī)則仍然適用,只不過(guò)延遲函數(shù)的參數(shù)是一個(gè)地址值,這種情況下,defer后面的語(yǔ)句對(duì)變量的修改會(huì)影響延遲函數(shù)。

    多個(gè)defer,執(zhí)行順序
    package main
    
    import (
        "fmt"
    )
    
    func main() {
        defer fmt.Println("main defer1")
        test()
        defer fmt.Println("main defer2")
    }
    
    func test() () {
        defer func() {
            fmt.Println("test defer1")
        }()
        defer func() {
            fmt.Println("test defer2")
        }()
    }

    輸出結(jié)果:

    test defer2
    test defer1
    main defer2
    main defer1

    總結(jié): 后進(jìn)先出(LIFO)的順序執(zhí)行,即先出現(xiàn)的 defer 最后執(zhí)行。即:多個(gè)defer語(yǔ)句的執(zhí)行順序是逆序執(zhí)行。

    defer的函數(shù)一定會(huì)執(zhí)行么?

    defer是Go語(yǔ)言中的延遲執(zhí)行語(yǔ)句,用來(lái)添加函數(shù)結(jié)束時(shí)執(zhí)行的代碼,常用于釋放某些已分配的資源、關(guān)閉數(shù)據(jù)庫(kù)連接、斷開(kāi)socket連接、解鎖一個(gè)加鎖的資源。

    Go語(yǔ)言機(jī)制擔(dān)保一定會(huì)執(zhí)行defer語(yǔ)句中的代碼。其它語(yǔ)言中也有類似的機(jī)制,比如Java、C#語(yǔ)言里的finally語(yǔ)句,C++語(yǔ)言里的析構(gòu)函數(shù)(Destructor)可以起類似的作用,C++語(yǔ)言機(jī)制擔(dān)保在對(duì)象被銷毀前一定會(huì)執(zhí)行析構(gòu)函數(shù)中的代碼。C++中的析構(gòu)函數(shù)析構(gòu)的是對(duì)象,Go中的defer析構(gòu)的是函數(shù)。

    panic情況

    網(wǎng)上demo:

    package main
    
    import (
    	"fmt"
    )
    
    func test1() {
    	fmt.Println("test")
    }
    
    func test2() {
    	panic(1)
    }
    func main() {
    	fmt.Println("main start")
    	defer test1()
    	test2() //造panic
    	fmt.Println("main end")
    }

    執(zhí)行結(jié)果:

    main start
    test                                                                  
    panic: 1                                                                                                                                   
    goroutine 1 [running]:                                                
    main.test2(...)         

    總結(jié): 我們發(fā)現(xiàn)正常的panic,還是會(huì)調(diào)我們的defer的,并且在會(huì)在panic之前執(zhí)行。

    os.Exit情況

    網(wǎng)上demo:

    package main
    
    import (
    	"fmt"
    	"os"
    )
    
    func test1() {
    	fmt.Println("test")
    }
    
    func main() {
    	fmt.Println("main start")
    	defer test1()
    	fmt.Println("main end")
    	os.Exit(0)
    }

    執(zhí)行結(jié)果:

    main start
    main end

    總結(jié): 如果在當(dāng)前函數(shù)里是因?yàn)閳?zhí)行了os.Exit退出,而不是正常return退出或者panic退出,那程序會(huì)立即停止,被defer的函數(shù)調(diào)用不會(huì)執(zhí)行。

    kill情況(Ctrl+C)
    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func test1() {
    	fmt.Println("test")
    }
    
    func test2() {
    	time.Sleep(60 * time.Second)
    }
    func main() {
    	fmt.Println("main start")
    	defer test1()
    	test2()
    	fmt.Println("main end")
    }

    執(zhí)行結(jié)果:

    main start

    Process finished with the exit code -1073741510 (0xC000013A: interrupted by Ctrl+C)

    如上,我們test2() 睡眠時(shí)間內(nèi),點(diǎn)擊Ctrl+C,發(fā)現(xiàn)defer test1()并沒(méi)有執(zhí)行。

    總結(jié):這個(gè)點(diǎn)很重要,需要我們?cè)谌粘.惓V袛鄷r(shí),留意defer是否未處理的情況。
    所以一般情況下,我們程序需要捕獲這種異常中斷,在程序退出前,手動(dòng)做一些處理。

    讀到這里,這篇“golang中的defer函數(shù)怎么用”文章已經(jīng)介紹完畢,想要掌握這篇文章的知識(shí)點(diǎn)還需要大家自己動(dòng)手實(shí)踐使用過(guò)才能領(lǐng)會(huì),如果想了解更多相關(guān)內(nèi)容的文章,歡迎關(guān)注億速云行業(yè)資訊頻道。

    向AI問(wèn)一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI