天天看點

Go defer關鍵字

文章目錄

  • ​​Go defer關鍵字​​

Go defer關鍵字

Go語言的defer會在目前函數傳回前執行傳入的函數,經常用于關閉檔案描述符,關閉資料庫連接配接等資源回收工作。

Go中defer主要有兩個問題:

  • defer調用時機和調用順序
  • defer調用函數使用傳值方式傳遞參數會進行預運算,計算結果會發生改變,最終進行計算的參數是運作時的結果

關于調用時機和調用順序

func main()  {
    fmt.Println("開始執行main函數")
    for i := 0;i < 5;i++{
        defer fmt.Println("執行defer,i=",i)
    }
    fmt.Println("main函數結束")
}      

結果:

Go defer關鍵字

調用時機:main函數執行完後,defer邏輯才開始執行

defer調用順序:先進後出,壓棧規則,先開始的defer後執行

func main()  {
    fmt.Println("main start")
    {
        fmt.Println("代碼塊start")
        defer fmt.Println("代碼塊中defer")
        fmt.Println("代碼塊end")
    }
    fmt.Println("main end")
}      

結果:

Go defer關鍵字

即使出現代碼塊,defer的作用域也是等整個函數結束後才執行

func main()  {
    num := 0
    fmt.Println("main start")
    defer fmt.Println(num)
    num = 2
    fmt.Println("main end")
}      

結果:

Go defer關鍵字

defer不會再執行時才會拷貝函數的參數值,而是一開始就會拷貝函數的參數值,是以這裡并不會得到2

func main()  {
    startTime := time.Now()
    fmt.Println("main start")
    defer func() {
        fmt.Println(time.Since(startTime))
    }()
    // main函數執行時間3秒
    time.Sleep(3 * time.Second)
    fmt.Println("main end")
}      

結果:

Go defer關鍵字

defer這裡是因為閉包,閉包擷取變量拷貝的是位址,而不是參數值

總結:defer調用的函數,參數的值在defer定義時就确定了,但如果值是位址,需要等到運作時才知道具體指向的值

func main()  {
    num := 0
    fmt.Println("main start")
    defer fmt.Println(num)
    fmt.Println("main end")
    os.Exit(0) // 執行os、Exit後,defer不會執行
}