中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何在Golang中延遲調用defer

發布時間:2020-12-23 14:03:23 來源:億速云 閱讀:222 作者:Leah 欄目:開發技術

如何在Golang中延遲調用defer ?很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

defer特性

1. 關鍵字 defer 用于注冊延遲調用。

2. 這些調用直到 return 前才被執。因此,可以用來做資源清理。

3. 多個defer語句,按先進后出的方式執行。

1.延遲調用

用法很簡單,只需要在函數前面加上 defer就行,就能實現將這個 該函數的調用延遲到當前函數執行完后再執行。例如:

package main 
import (
 "fmt"
)
func myFunc(){
 fmt.Println("minger")
}
func main(){
 defer myFunc() //等價于defer fmt.Println("minger")
 fmt.Println("程序猿編碼")
}

編譯運行:

如何在Golang中延遲調用defer

2.defer 與 return 孰先孰后

defer 和 return 到底是哪個先調用?先看看例子:

package main 
import (
 "fmt"
)
var name string = "go"
func myFunc() string {
  defer func() {
    name = "python"
  }()
  fmt.Println("myFunc 函數里的name:", name)
  return name
}
func main() {
  myName := myFunc()
  fmt.Println("main 函數里的name: ", name)
  fmt.Println("main 函數里的myname: ", myName )

編譯運行:

如何在Golang中延遲調用defer

來看看打印信息,第一行輸出,name 此時還是全局變量,值還是go

第二行輸出,在 defer 里改變了全局變量,此時name的值已經變成了 python

重點在第三行,為什么輸出的是 go ?

解釋只有一個,那就是 defer 是return 后才調用的。所以在執行 defer 前,myName 已經被賦值成 go 了。

3.多個defer 逆序執行

還是老規矩先來上代碼,看看輸出信息,例子:

package main 
import (
 "fmt"
)
func main(){
 name := "go"
 defer fmt.Println(name)
 name = "C/C++"
 defer fmt.Println(name)
 name = "Python"
 fmt.Println(name)
}

編譯輸出:

如何在Golang中延遲調用defer

可見 多個defer 是它們會以逆序執行(類似棧,即后進先出)。

defer官方的解釋

Each time a “defer” statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked. Instead, deferred functions are invoked immediately before the surrounding function returns, in the reverse order they were deferred. If a deferred function value evaluates to nil, execution panics when the function is invoked, not when the “defer” statement is executed.

翻譯一下:

每次defer語句執行的時候,會把函數“壓棧”,函數參數會被拷貝下來;當外層函數(非代碼塊,如一個for循環)退出時,defer函數按照定義的逆序執行;如果defer執行的函數為nil, 那么會在最終調用函數的產生panic.

為什么需要defer?

往往我們在編程的時候,經常需要打開一些資源,比如數據庫連接、文件、鎖等,這些資源需要在用完之后釋放掉,否則會造成內存泄漏。

因此我們有時會忘記關閉這些資源。Golang直接在語言層面提供defer關鍵字,在打開資源語句的下一行,就可以直接用defer語句來注冊函數結束后執行關閉資源的操作。

defer用途

1. 關閉文件句柄

2. 鎖資源釋放

3. 數據庫連接釋放

defer的使用其實非常簡單,來看看一個簡單用途:

package main
import (
 "log"
 "os"
)
func main() {
 f, err := os.OpenFile("text.txt", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666) //文件沒有就創建,文件存在就追加
 if err != nil {
 log.Fatal(err)
 }
 defer f.Close()
 f.WriteString("程序猿編碼\n")
}

編譯輸出:

如何在Golang中延遲調用defer

在打開文件的語句附近,用defer語句關閉文件。這樣,在函數結束之前,會自動執行defer后面的語句來關閉文件。

當然,defer會有小小地延遲,對時間要求特別特別特別高的程序,可以避免使用它。

總結

defer 語句經常使用于成對的操作,比如打開和關閉,連接和斷開,加鎖和解鎖,即便是再復雜的控制流,資源在任何情況下都能夠正確釋放。

補充:Golang中defer的三個實戰要點

前言

Golang中的defer是使用頻次比較高的,能創造出延遲生效特效的一種方式。

defer也有自己的矯情,需要注意的。

本文將從通過代碼的方式來說明defer的三點矯情。

1.defer的生效順序

2.defer與return,函數返回值之間的順序

3.defer定義和執行兩個步驟,做的事情。

正文

1.defer的生效順序

先說結論:defer的執行順序是倒序執行(同入棧先進后出)

func main() {
 defer func() {
 fmt.Println("我后出來")
 }()
 defer func() {
 fmt.Println("我先出來")
 }()
}

執行后打印出:

我先出來

我后出來

2.defer與return,函數返回值之間的順序

先說結論:return最先執行->return負責將結果寫入返回值中->接著defer開始執行一些收尾工作->最后函數攜帶當前返回值退出

返回值的表達方式,我們知道根據是否提前聲明有兩種方式:一種是func test() int 另一種是 func test() (i int),所以兩種情況都來說說

func test() int
func main() {
 fmt.Println("main:", test())
}
func test() int {
 var i int
 defer func() {
 i++
 fmt.Println("defer2的值:", i)
 }()
 defer func() {
 i++
 fmt.Println("defer1的值:", i)
 }()
 return i
}

輸出:

defer1的值: 1

defer2的值: 2

main: 0

詳解:return的時候已經先將返回值給定義下來了,就是0,由于i是在函數內部聲明所以即使在defer中進行了++操作,也不會影響return的時候做的決定。

func test() (i int)
func main() {
 fmt.Println("main:", test())
}
func test() (i int) {
 defer func() {
 i++
 fmt.Println("defer2的值:", i)
 }()
 defer func() {
 i++
 fmt.Println("defer1的值:", i)
 }()
 return i
}

輸出:

defer1的值: 1

defer2的值: 2

main: 2

詳解:由于返回值提前聲明了,所以在return的時候決定的返回值還是0,但是后面兩個defer執行后進行了兩次++,將i的值變為2,待defer執行完后,函數將i值進行了返回。

3.defer定義和執行兩個步驟,做的事情

先說結論:會先將defer后函數的參數部分的值(或者地址)給先下來【你可以理解為()里頭的會先確定】,后面函數執行完,才會執行defer后函數的{}中的邏輯

func test(i *int) int {
 return *i
}
func main(){
 var i = 1
 // defer定義的時候test(&i)的值就已經定了,是1,后面就不會變了
 defer fmt.Println("i1 =" , test(&i))
 i++
 // defer定義的時候test(&i)的值就已經定了,是2,后面就不會變了
 defer fmt.Println("i2 =" , test(&i))
 // defer定義的時候,i就已經確定了是一個指針類型,地址上的值變了,這里跟著變
 defer func(i *int) {
 fmt.Println("i3 =" , *i)
 }(&i)
 // defer定義的時候i的值就已經定了,是2,后面就不會變了
 defer func(i int) {
 //defer 在定義的時候就定了
 fmt.Println("i4 =" , i)
 }(i)
 defer func() {
 // 地址,所以后續跟著變
 var c = &i
 fmt.Println("i5 =" , *c)
 }()
 
 // 執行了 i=11 后才調用,此時i值已是11
 defer func() {
 fmt.Println("i6 =" , i)
 }()
 i = 11
}

看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

花莲市| 霍林郭勒市| 临洮县| 彭水| 武义县| 耿马| 苏尼特右旗| 桓仁| 安康市| 阿城市| 九龙县| 托克逊县| 永春县| 福泉市| 两当县| 临城县| 繁峙县| 静海县| 前郭尔| 尼勒克县| 沙雅县| 达日县| 利川市| 萍乡市| 松阳县| 元氏县| 峨眉山市| 中山市| 土默特右旗| 和林格尔县| 琼结县| 喀喇沁旗| 天峨县| 沁水县| 南陵县| 乌苏市| 绥滨县| 永新县| 长沙县| 荃湾区| 岐山县|