1. 程式人生 > >Go語言defer分析

Go語言defer分析

什麼是defer?

defer語句是專門在函式結束以後做一些清理工作的。我們先舉一個例子來更好的理解,現在有一個函式,它的作用是把一個檔案內容拷貝到另一個檔案。

func CopyFile(dstName string, srcName string) (written int64, err error) {
    src, err := os.Open(srcName)
    if err != nil {
        return
    }

    dst, err := os.Create(dstName)
    if err != nil {
        return
    }

    written, err = io.Copy(dst, src)
    src.Close()
    dst.Close()
    return
}

以上程式碼是可以正常執行的,但是存在一個問題,如果os.Create執行失敗,那麼就無法執行到檔案資源的Close函式。程序每開啟一個檔案就會佔用一個檔案描述符,而在系統當中,檔案描述符是有上限的,可以通過ulimit -n檢視,如果資源沒有被及時釋放,會出現資源浪費的情況。如果開啟檔案過多,也會出現Too many open files的提示。這個時候就需要通過defer來解決問題了,程式碼如下。

func CopyFile(dstName string, srcName string) (written int64, err error) {
    src, err := os.Open(srcName)
    if err != nil {
        return
    }
    defer src.Close()

    dst, err := os.Create(dstName)
    if err != nil {
        return
    }
    defer dst.Close()

    written, err = io.Copy(dst, src)
    return
}

defer語句會在return引數設定之後、函式返回給呼叫者之前執行,這樣就不再擔心檔案資源無法被Close了。

defer的三個規則

規則一:被deferred的函式引數在defer時確定

func a() {
    i := 0
    defer fmt.Println(i)
    i++
    return
}

我們通過以上程式碼來理解這個拗口的規則,如果根據官方的定義來理解這段程式碼,變數i的值鐵定為1,但實際執行的結果不是1,卻是0。

Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked. Instead, deferred functions are invoked immediately before the surrounding function returns, in the reverse order they were deferred.

那我們再重新來理解這個規則,變數i是在逐行執行到defer語句的時候就已經確定了值,這個時候變數i還沒有進行自增,所以輸出的結果應該是0而不是1。

規則二:被deferred函式執行順序遵循LIFO原則

func b() {
    for i := 0; i < 4; i++ {
        defer fmt.Print(i)
    }
}

LIFO全稱為Last In First Out,意為後進先出,棧是一種典型的LIFO資料結構。defer也是如此,拿以上程式碼為例,先後遍歷了四次,也就是做了四次壓棧操作。同理,在函式return之前,就會逐一出棧,倒序執行defer語句,所以上述程式碼輸出內容為3210

規則三:deferred函式可以讀取和修改函式的返回值

func c() (i int) {
    defer func() { i++ }()
    return 1
}

我們定義一個defer函式,將變數i自增,那麼最終變數i的值為2。我們從官方文件中可以看出,defer語句是在函式設定返回值後,且在返回給主調函式前執行的,根據這個思路,c函式已經return了1,這個時候執行了defer函式,將返回的結果1進行了自增,然後返回給主調函式,這個時候主調函式拿到的值就是2了。

deferred functions are executed after any result parameters are set by that return statement but before the function returns to its caller