1. 程式人生 > >Golang入門教程(十三)延遲函數defer詳解

Golang入門教程(十三)延遲函數defer詳解

它的 ref lang back 教程 執行順序 ont 例子 返回值

前言

  大家都知道go語言的defer功能很強大,對於資源管理非常方便,但是如果沒用好,也會有陷阱哦。Go 語言中延遲函數 defer 充當著 try...catch 的重任,使用起來也非常簡便,然而在實際應用中,很多 gopher 並沒有真正搞明白 defer、return、返回值、panic 之間的執行順序,從而掉進坑中,今天我們就來揭開它的神秘面紗!話不多說了,來一起看看詳細的介紹吧。

基本介紹

延時調用函數的語法如下:

defer func_name(param-list)

當一個函數調用前有關鍵字 defer 時, 那麽這個函數的執行會推遲到包含這個 defer 語句的函數即將返回前才執行. 例如:

package main

import (
	"fmt"
)

func main() {
  	defer fmt.Println("我是最後執行的")
        fmt.Println("我是第一個")
        fmt.Println("我是第二個")
}

執行結果

技術分享圖片

小結:defer 調用的函數參數的值 defer 被定義時就確定了

案例2

package main

import "fmt"

func main() {
	i := 1
	defer fmt.Println("Deferred print:",i)
	i++
	fmt.Println("Normal print:", i)
}

技術分享圖片

在 "defer fmt.Println("Deferred print:", i)" 調用時, i 的值已經確定了, 因此相當於 defer fmt.Println("Deferred print:", 1)了

小結:需要強調的時, defer 調用的函數參數的值在 defer 定義時就確定了, 而 defer 函數內部所使用的變量的值需要在這個函數運行時才確定

案例3

package main

import "fmt"

func f1() (r int) {
    r = 1
    defer func() {
        r++
        fmt.Println(" r value = ",r)
    }()
    r = 2
    return
}

func main() {
    f1()
}

技術分享圖片

小結:上面的例子中, 最終打印的內容是 "3", 這是因為在 "r = 2" 賦值之後, 執行了 defer 函數, 因此在這個函數內, r 的值是2了, 自增後變為3

函數定義

func function_name( [parameter list] ) [return_types] {
   函數體
}

或者

  func funcName(形參1 type[, 形參2 type...]) [([[返回變量1] type[, [返回變量2] type...]])] {
    [return [返回變量1[, 返回變量2...]]]
  }

defer 順序

如果有多個defer 調用, 則調用的順序是先進後出的順序, 類似於入棧出棧一樣(後進先出/先進後出) 

案例4

package main

import "fmt"

func f3() (r int) {
    defer func() {
        r++
    }()
    return 0
}

func main() {
    fmt.Println(f3())
}

技術分享圖片

上面 fmt.Println(f1()) 打印的是什麽呢? 很多朋友可能會認為打印的是0, 但是正確答案是 1. 這是為什麽呢?
要弄明白這個問題, 我們需要牢記兩點:

  • defer 函數調用的執行時機是外層函數設置返回值之後, 並且在即將返回之前
  • return XXX 操作並不是原子的

匿名返回值

案例5

package main

import "fmt"

func main() {
	fmt.Println("a return:", a()) // 打印結果為 a return: 0
}

func a() int {
	var i int

	defer func() {
		i++
		fmt.Println("a defer2:", i) // 打印結果為 a defer2: 2
	}()

	defer func() {
		i++
		fmt.Println("a defer1:", i) // 打印結果為 a defer1: 1
	}()

	return i
}

技術分享圖片

參考

1、golang defer 使用小結與註意要點

2、Go語言中的延遲函數defer示例詳解

  

Golang入門教程(十三)延遲函數defer詳解