【go語言】wait,wait for me
去年輸出了一系列golang的編碼文章,但總感覺有話沒講,但又不清楚具體是什麼,所以本文以隨筆為主。
我們知道函式的呼叫其實就是一個入棧和出棧的動作:
main() --> normal()
如果用這個表示呼叫,那麼在堆疊中就是把函式normal()的入口地址push,當函式normal()執行完畢後,堆疊pop函式normal()地址,同時返回到main()的呼叫處。
試想當執行函式normal()時出現異常時,會有什麼情況發生呢?
package main
import (
"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好玩吧,上面的例子有的來源自於網路上其他人的部落格。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/964/viewspace-2818270/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- wait() vs sleep()AI
- Golang CLOSE WAIT 分析GolangAI
- Oracle RAC Wait EventsOracleAI
- 【YashanDB知識庫】YAS-02024 lock wait timeout, wait time 0 millisecondsAI
- sleep()和wait()區別AI
- 10.25 V$SESSION_WAITSessionAI
- cursor pin S wait on XAI
- [20191223]Wait for Java.txtAIJava
- Oracle Enqueues Wait Events 二OracleENQAI
- Oracle Enqueues Wait Events 一OracleENQAI
- Oracle Enqueues Wait Events 三OracleENQAI
- cursor: pin S wait on XAI
- MXNet: wait_to_read 方法AI
- 調整time_waitAI
- TCP連線的TIME_WAIT和CLOSE_WAIT 狀態解說TCPAI
- 10.27 V$SESSION_WAIT_HISTORYSessionAI
- 10.26 V$SESSION_WAIT_CLASSSessionAI
- socket close和shutdown的區別,TIME_WAIT和CLOSE_WAIT,SO_REUSEADDRAI
- 條件佇列大法好:wait和notify的基本語義佇列AI
- Lock wait timeout exceeded; try restarting transactionAIREST
- mysql MASTER_POS_WAIT函式MySqlASTAI函式
- session檢視中wait_timeSessionAI
- 阻塞程式函式 wait()和waitpid()函式AI
- 聊聊select, poll 和 epoll_waitAI
- MySQL中wait_timeout的坑MySqlAI
- Go語言————1、初識GO語言Go
- wait()方法與await()方法的區別AI
- cursor:pin S wait on X故障診分析AI
- Java-併發-wait()、notify()和notifyAll()JavaAI
- MySQL更新卡住報錯lock wait timeoutMySqlAI
- SharePlex reader missed marker wait for xx but got xxAIGo
- 【WAIT】 log file sync等待事件說明AI事件
- stored procedure 收集session wait 資訊(轉)SessionAI
- Java 中的 Wait 和 Notify 機制JavaAI
- [20210527]關於v$wait_chain.txtAI
- axios.get (url).wait (loadingGif, functionOk, functionErr)iOSAIFunction
- buffer busy wait 等待事件說明(轉)AI事件
- 大量time-wait的處理方法AI