Golang defer 使用時的坑

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

defer是golang語言中的關鍵字,用於資源的釋放,會在函數返回之前進行調用。一般採用如下模式:

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

如果有多個defer運算式,調用順序類似於棧,越後面的defer運算式越先被調用。

不過如果對defer的瞭解不夠深入,使用起來可能會踩到一些坑,尤其是跟帶命名的返回參數一起使用時。在講解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}

請讀者先不要運行代碼,在心裡跑一遍結果,然後去驗證。

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

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

函數返回的過程是這樣的:先給傳回值賦值,然後調用defer運算式,最後才是返回到調用函數中。

defer運算式可能會在設定函數傳回值之後,在返回到調用函數之前,修改傳回值,使最終的函數傳回值與你想象的不一致。

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

傳回值 = xxx調用defer函數空的return

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

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

所以這個傳回值是1。

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

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

所以這個的結果是5。

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

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

所以這個例子的結果是1。

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

defer的實現

defer關鍵字的實現跟go關鍵字很類似,不同的是它調用的是runtime.deferproc而不是runtime.newproc。

在defer出現的地方,插入了指令call runtime.deferproc,然後在函數返回之前的地方,插入指令call runtime.deferreturn。

普通的函數返回時,彙編代碼類似:

add xx SPreturn

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

call runtime.deferreturn,add xx SPreturn

goroutine的控制結構中,有一張表記錄defer,調用runtime.deferproc時會將需要defer的運算式記錄在表中,而在調用runtime.deferreturn的時候,則會依次從defer表中出棧並執行。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.