【go語言】wait,wait for me

去年輸出了一系列golang的編碼文章,但總感受有話沒講,但又不清楚具體是什麼,因此本文以隨筆爲主。
golang



咱們知道函數的調用其實就是一個入棧和出棧的動做:
網絡

main() --> normal()ide

若是用這個表示調用,那麼在堆棧中就是把函數normal()的入口地址push,當函數normal()執行完畢後,堆棧pop函數normal()地址,同時返回到main()的調用處。函數


試想當執行函數normal()時出現異常時,會有什麼狀況發生呢?編碼

package mainspa


import (orm

    "fmt"內存

)資源


func normal(a int64) int64 {字符串

        var b int64 = 0

        return a / b

}


func main() {

        var a int64 = 64

        fmt.Println("a/b= ", normal(a))

}

你確定想,這會拋錯呀~

是的,U are right,會拋出panic: runtime error: integer divide by zero.


進一步想,若是var b int64 = 0不是簡單的賦值,而是一塊內存的分配,不幸的是,剛分配完內存就拋異常了,那麼該內存就永遠沒有被釋放的機會。


如何解決?其它語言有:try、catch、finally等關鍵字。

golang採用了defer關鍵字,該關鍵字用於告訴程序:「wait,wait,我作點事情以後,你再退出本次調用」。



func normal(a int64) int64 {

        defer fmt.Println("wait, wait for me.")

        var b int64 = 0

        return a / b

}

修改normal()函數,在函數體內增長defer再運行,就會發如今拋異常以前把「wait, wait for me.」打印出來了。


這表示在函數normal()被調用以後,64/0遇到了問題,此時golang會拋出panic前,defer說等等,等我打印點東西后,你再拋。



【defer語法】:

defer 表達式


func normal(a int64) int64 {

        defer func() {

                 fmt.Println("panic will be throwen.")

        }()

        var b int64 = 0

        return a / b

}

當表達式是一個匿名函數時,必定要記得後面追加(),這表示是一個表達式 :)



【defer使用場景】:

defer通常使用在函數體開始,或者緊跟着申請資源的語句後面

不建議把defer放到函數體的後面。修改一下上面的示例:

func normal(a int64) {

var b int64 = 0

fmt.Println("a/b= ", a/b)

defer func() {

fmt.Println("panic will be throwen.")

}()

}


func main() {

var a int64 = 64

normal(a)

}

此時的defer已無心義,因此"panic will be throwen."不會被打印出來。




【多個順序defer】:

被調用函數中如有多個順序defer,則先會出現「先定義後執行」現象

func main() {

defer fmt.Println("0")

defer fmt.Println("1")

defer fmt.Println("2")

defer fmt.Println("3")

defer fmt.Println("4")

fmt.Println("Test multi defers")

}

執行結果爲:

Test multi defers

4

3

2

1

0

想一想這很天然,從堆棧來看,越是後面定義的defer越是處於堆棧的棧頂。


該代碼能夠精簡爲:

func main() {

        for i := 0; i < 5; i++ {

              defer fmt.Println(i)

        }

        fmt.Println("Test multi defers")

}



【defer表達式中存在函數調用】:

defer語句被執行的時候,傳遞給延遲函數的參數都會被求值,可是延遲函數調用表達式並不會在此時被求值


感受這句話比較繞口,很差難理解?先看一個例子:

func history(date string) string {  // 打印"2016 will be history",並返回"2017"字符串

s := date + " will be history."

fmt.Println(s)

return "2017"

}


func future(date string) string {  // 打印"2017 will be coming",並返回"2017 will be coming"字符串

s := date + " will be coming."

fmt.Println(s)

return s

}


func main() {

defer future(history("2016"))

fmt.Println("It's the Spring Festival now.")

}

對照着defer future(history("2016"))理解一下「傳遞給延遲函數的參數都會被求值,可是延遲函數調用表達式並不會在此時被求值」。

  • 延遲函數:future()

  • 延遲函數的參數:history("2016")

因爲延遲函數的參數會被求值,即history("2016")會被執行,因此會先指印出「2016 will be history」,同時延遲函數變爲future("2017"),它要求被延遲執行。

從而該程序執行結果爲:

2016 will be history.

It's the Spring Festival now.

2017 will be coming.


感受「defer語句被執行的時候,傳遞給延遲函數的參數都會被求值,可是延遲函數調用表達式並不會在此時被求值」這語句已經理解了,請再看下面的例子:

func main() {

for i := 0; i < 5; i++ {

defer func() {

fmt.Println(i)

}()

}

fmt.Println("Test multi defers")

}

它的運行結果爲:

Test multi defers

5

5

5

5

5

是否是有點懵逼了?

對照着defer func(){

                    fmt.Println(i)

          }()

理解一下「傳遞給延遲函數的參數都會被求值,可是延遲函數調用表達式並不會在此時被求值

  • 延遲函數表達式:

       func() {

               fmt.Println(i)

       }()

在defer語句被執行時,該表達式並不會被求值,即被執行,i值你本身玩吧,因此等循環完成以後i值變爲5,再打印出「Test multi defers」,函數立刻要return時,這5個defer分別說:「wait, wait for me.」。

因而第5個defer表達式被執行,打印i值(這時i值爲5),因此打印出:

5

因而第4個defer表達式被執行,打印i值(這時i值爲5),因此打印出:

5

因而第3個defer表達式被執行,打印i值(這時i值爲5),因此打印出:

5

因而第2個defer表達式被執行,打印i值(這時i值爲5),因此打印出:

5

因而第1個defer表達式被執行,打印i值(這時i值爲5),因此打印出:

5

從而出現該結果 :)



接下來咋玩?

func main() {

for i := 0; i < 5; i++ {

defer func(n int) {

fmt.Println(n)

}(i)

}

fmt.Println("Test multi defers")

}

再理解"defer語句被執行的時候,傳遞給延遲函數的參數都會被求值,可是延遲函數調用表達式並不會在此時被求值"一下:

當i=0時

  • 延遲函數調用表達式:func(n int) { fmt.Println(n) }(i)

  • 延遲函數的參數:n

延遲函數調用表達式不會被求值,但延遲函數的參數i會被求值,因此n值變爲0


當i=1時

  • 延遲函數調用表達式:func(n int) { fmt.Println(n) }(i)

  • 延遲函數的參數:n

延遲函數調用表達式不會被求值,但延遲函數的參數i會被求值,因此n值變爲1


依次類推,從而最終執行結果爲:

Test multi defers

4

3

2

1

0



defer好玩吧,上面的例子有的來源自於網絡上其餘人的博客。

相關文章
相關標籤/搜索