首頁 >後端開發 >Golang >go defer(go延遲函數)介紹

go defer(go延遲函數)介紹

尚
轉載
2019-11-30 14:03:382323瀏覽

go defer(go延遲函數)介紹

Go語言的defer算是語言的新特性,至少對比當今主流程式語言如此。

defer語句調用一個函數,這個函數執行會推遲,直到外圍的函數返回,或者外圍函數運行到最後,或者相應的goroutine panic

每當defer執行的時候,它後面的函數值(在go中函數是一個引用類型,是一等公民,可以賦值給變數)和函數參數會被求值,但是函數不會立即調用,直到(↑)上述三種情況發生。這就是defer的全部內容,沒了,剩下就是defer的best practice

#函數不會立即呼叫

先從最簡單的開始:

func readFile(fileName string){
    f,err := os.Open(fileName)
    if err!=nil {
        return
    }
    defer f.Close()
    var content [1024]byte
    f.Read(content[:])
    fmt.Printf("%s",content)
}
func main() {
    readFile("test.data")
}

程式輸出test.data前1024位元組的內容。值得一提的是,類似這種open/close配對操作是defer的慣用法。這個例子詮釋了上面那句話的後半段

"但是函數不會被呼叫" 

因為如果defer後面的f.Close()沒有延遲執行,那麼檔案描述符都關閉了,就不會讀取任何內容。

函數值和函數參數被求值,但函數不會立即呼叫

#下面這個例子即將詮釋上半段,它來自a8093152e673feb7aba1828c43532094,稍作修改:

func trace(funcName string) func(){
    start := time.Now()
    fmt.Printf("function %s enter\n",funcName)
    return func(){
        log.Printf("function %s exit (elapsed %s)",funcName,time.Since(start))
    }
}
 
func foo(){
    defer trace("foo()")()
    time.Sleep(5*time.Second)
}
func main(){
    foo()
    foo()
}
/*
OUTPUT:
function foo() enter
function foo() exit (elapsed 5.0095471s)
function foo() enter
function foo() exit (elapsed 5.0005382s)
*/

為什麼foo會輸出enter然後等待五秒左右再輸出exit? 因為正如我們說的,

defer後面的函數值和參數會被求值但是實際函數呼叫卻要等到最後

這裡函數值就是trace()回傳的匿名函數,函數參數當然就是字串字面值"foo()", 對trace("foo()")的求值會輸出function foo() enter, 實際函數呼叫trace("foo()")()即輸出function foo() exit(elapsed x.x)會延遲到return執行(如果return會更新回傳值變量,則會在更新後才執行defer的函數)。

雜項

多說一點,如果存在多個defer語句,最後的defer的函數的執行順序與defer出現的順序相反,如:

func main() {
    func1 := func(){
        fmt.Println("func1() execution deferred")
    }
    func2 := func(){
        fmt.Println("func2() execution deferred")
    }
    defer func1()
    defer func2()
    fmt.Println("strat\nworking...")
}
/*
OUTPUT:
strat
working...
func2() execution deferred
func1() execution deferred
*/

以上是go defer(go延遲函數)介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:cnblogs.com。如有侵權,請聯絡admin@php.cn刪除