99re热这里只有精品视频,7777色鬼xxxx欧美色妇,国产成人精品一区二三区在线观看,内射爽无广熟女亚洲,精品人妻av一区二区三区

Go語言 關(guān)鍵字:defer

2018-07-25 17:23 更新

defer和go一樣都是Go語言提供的關(guān)鍵字。defer用于資源的釋放,會在函數(shù)返回之前進行調(diào)用。一般采用如下模式:

f,err := os.Open(filename)
if err != nil {
    panic(err)
}
defer f.Close()

如果有多個defer表達式,調(diào)用順序類似于棧,越后面的defer表達式越先被調(diào)用。

不過如果對defer的了解不夠深入,使用起來可能會踩到一些坑,尤其是跟帶命名的返回參數(shù)一起使用時。在講解defer的實現(xiàn)之前先看一看使用defer容易遇到的問題。

defer使用時的坑

先來看看幾個例子。例1:

func f() (result int) {
    defer func() {
        result++
    }()
    return 0
}

例2:

func f() (r int) {
     t := 5
     defer func() {
       t = t + 5
     }()
     return t
}

例3:

func f() (r int) {
    defer func(r int) {
          r = r + 5
    }(r)
    return 1
}

請讀者先不要運行代碼,在心里跑一遍結(jié)果,然后去驗證。

例1的正確答案不是0,例2的正確答案不是10,如果例3的正確答案不是6......

defer是在return之前執(zhí)行的。這個在 官方文檔中是明確說明了的。要使用defer時不踩坑,最重要的一點就是要明白,return xxx這一條語句并不是一條原子指令!

函數(shù)返回的過程是這樣的:先給返回值賦值,然后調(diào)用defer表達式,最后才是返回到調(diào)用函數(shù)中。

defer表達式可能會在設(shè)置函數(shù)返回值之后,在返回到調(diào)用函數(shù)之前,修改返回值,使最終的函數(shù)返回值與你想象的不一致。

其實使用defer時,用一個簡單的轉(zhuǎn)換規(guī)則改寫一下,就不會迷糊了。改寫規(guī)則是將return語句拆成兩句寫,return xxx會被改寫成:

返回值 = xxx
調(diào)用defer函數(shù)
空的return

先看例1,它可以改寫成這樣:

func f() (result int) {
     result = 0  //return語句不是一條原子調(diào)用,return xxx其實是賦值+ret指令
     func() { //defer被插入到return之前執(zhí)行,也就是賦返回值和ret指令之間
         result++
     }()
     return
}

所以這個返回值是1。

再看例2,它可以改寫成這樣:

func f() (r int) {
     t := 5
     r = t //賦值指令
     func() {        //defer被插入到賦值與返回之間執(zhí)行,這個例子中返回值r沒被修改過
         t = t + 5
     }
     return        //空的return指令
}

所以這個的結(jié)果是5。

最后看例3,它改寫后變成:

func f() (r int) {
     r = 1  //給返回值賦值
     func(r int) {        //這里改的r是傳值傳進去的r,不會改變要返回的那個r值
          r = r + 5
     }(r)
     return        //空的return
}

所以這個例子的結(jié)果是1。

defer確實是在return之前調(diào)用的。但表現(xiàn)形式上卻可能不像。本質(zhì)原因是return xxx語句并不是一條原子指令,defer被插入到了賦值 與 ret之間,因此可能有機會改變最終的返回值。

defer的實現(xiàn)

defer關(guān)鍵字的實現(xiàn)跟go關(guān)鍵字很類似,不同的是它調(diào)用的是runtime.deferproc而不是runtime.newproc。

在defer出現(xiàn)的地方,插入了指令call runtime.deferproc,然后在函數(shù)返回之前的地方,插入指令call runtime.deferreturn。

普通的函數(shù)返回時,匯編代碼類似:

add xx SP
return

如果其中包含了defer語句,則匯編代碼是:

call runtime.deferreturn,
add xx SP
return

goroutine的控制結(jié)構(gòu)中,有一張表記錄defer,調(diào)用runtime.deferproc時會將需要defer的表達式記錄在表中,而在調(diào)用runtime.deferreturn的時候,則會依次從defer表中出棧并執(zhí)行。

links


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號