本篇內(nèi)容介紹了“go語言中的defer延遲函數(shù)是什么”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!
創(chuàng)新互聯(lián)公司是專業(yè)的桐城網(wǎng)站建設(shè)公司,桐城接單;提供成都網(wǎng)站建設(shè)、成都網(wǎng)站制作,網(wǎng)頁設(shè)計,網(wǎng)站設(shè)計,建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進行桐城網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團隊,希望更多企業(yè)前來合作!
Go 語言中有個 defer 關(guān)鍵字,常用于實現(xiàn)延遲函數(shù)來保證關(guān)鍵代碼的最終執(zhí)行,常言道: "未雨綢繆方可有備無患".
延遲函數(shù)就是這么一種機制,無論程序是正常返回還是異常報錯,只要存在延遲函數(shù)都能保證這部分關(guān)鍵邏輯最終執(zhí)行,所以用來做些資源清理等操作再合適不過了.
日常開發(fā)編程中,有些操作總是成雙成對出現(xiàn)的,有開始就有結(jié)束,有打開就要關(guān)閉,還有一些連續(xù)依賴關(guān)系等等.
一般來說,我們需要控制結(jié)束語句,在合適的位置和時機控制結(jié)束語句,手動保證整個程序有始有終,不遺漏清理收尾操作.
最常見的拷貝文件操作大致流程如下:
打開源文件
srcFile, err := os.Open("fib.txt")
if err != nil {
t.Error(err)
return
} 創(chuàng)建目標文件
dstFile, err := os.Create("fib.txt.bak")
if err != nil {
t.Error(err)
return
}拷貝源文件到目標文件
io.Copy(dstFile, srcFile)
關(guān)閉目標文件
dstFile.Close() srcFile.Close()
關(guān)閉源文件
srcFile.Close()
值得注意的是: 這種拷貝文件的操作需要特別注意操作順序而且也不要忘記釋放資源,比如先打開再關(guān)閉等等!
func TestCopyFileWithoutDefer(t *testing.T) {
srcFile, err := os.Open("fib.txt")
if err != nil {
t.Error(err)
return
}
dstFile, err := os.Create("fib.txt.bak")
if err != nil {
t.Error(err)
return
}
io.Copy(dstFile, srcFile)
dstFile.Close()
srcFile.Close()
}> 「雪之夢技術(shù)驛站」: 上述代碼邏輯還是清晰簡單的,可能不會忘記釋放資源也能保證操作順序,但是如果邏輯代碼比較復(fù)雜的情況,這時候就有一定的實現(xiàn)難度了!
可能是為了簡化類似代碼的邏輯,Go 語言引入了 defer 關(guān)鍵字,創(chuàng)造了"延遲函數(shù)"的概念.
無 defer 的文件拷貝
func TestCopyFileWithoutDefer(t *testing.T) {
if srcFile, err := os.Open("fib.txt"); err != nil {
t.Error(err)
return
} else {
if dstFile,err := os.Create("fib.txt.bak");err != nil{
t.Error(err)
return
}else{
io.Copy(dstFile,srcFile)
dstFile.Close()
srcFile.Close()
}
}
}有 defer 的文件拷貝
func TestCopyFileWithDefer(t *testing.T) {
if srcFile, err := os.Open("fib.txt"); err != nil {
t.Error(err)
return
} else {
defer srcFile.Close()
if dstFile, err := os.Create("fib.txt.bak"); err != nil {
t.Error(err)
return
} else {
defer dstFile.Close()
io.Copy(dstFile, srcFile)
}
}
}上述示例代碼簡單展示了 defer 關(guān)鍵字的基本使用方式,顯著的好處在于 Open/Close 是一對操作,不會因為寫到最后而忘記 Close 操作,而且連續(xù)依賴時也能正常保證延遲時機.
簡而言之,如果函數(shù)內(nèi)部存在連續(xù)依賴關(guān)系,也就是說創(chuàng)建順序是 A->B->C 而銷毀順序是 C->B->A.這時候使用 defer 關(guān)鍵字最合適不過.
> 官方文檔相關(guān)表述見 Defer statements
如果沒有 defer 延遲函數(shù)前,普通函數(shù)正常運行:
func TestFuncWithoutDefer(t *testing.T) {
// 「雪之夢技術(shù)驛站」: 正常順序
t.Log("「雪之夢技術(shù)驛站」: 正常順序")
// 1 2
t.Log(1)
t.Log(2)
}當添加 defer 關(guān)鍵字實現(xiàn)延遲后,原來的 1 被推遲到 2 后面而不是之前的 1 2 順序.
func TestFuncWithDefer(t *testing.T) {
// 「雪之夢技術(shù)驛站」: 正常順序執(zhí)行完畢后才執(zhí)行 defer 代碼
t.Log(" 「雪之夢技術(shù)驛站」: 正常順序執(zhí)行完畢后才執(zhí)行 defer 代碼")
// 2 1
defer t.Log(1)
t.Log(2)
}如果存在多個 defer 關(guān)鍵字,執(zhí)行順序可想而知,越往后的越先執(zhí)行,這樣才能保證按照依賴順序依次釋放資源.
func TestFuncWithMultipleDefer(t *testing.T) {
// 「雪之夢技術(shù)驛站」: 猜測 defer 底層實現(xiàn)數(shù)據(jù)結(jié)構(gòu)可能是棧,先進后出.
t.Log(" 「雪之夢技術(shù)驛站」: 猜測 defer 底層實現(xiàn)數(shù)據(jù)結(jié)構(gòu)可能是棧,先進后出.")
// 3 2 1
defer t.Log(1)
defer t.Log(2)
t.Log(3)
}相信你已經(jīng)明白了多個 defer 語句的執(zhí)行順序,那就測試一下吧!
func TestFuncWithMultipleDeferOrder(t *testing.T) {
// 「雪之夢技術(shù)驛站」: defer 底層實現(xiàn)數(shù)據(jù)結(jié)構(gòu)類似于棧結(jié)構(gòu),依次倒敘執(zhí)行多個 defer 語句
t.Log(" 「雪之夢技術(shù)驛站」: defer 底層實現(xiàn)數(shù)據(jù)結(jié)構(gòu)類似于棧結(jié)構(gòu),依次倒敘執(zhí)行多個 defer 語句")
// 2 3 1
defer t.Log(1)
t.Log(2)
defer t.Log(3)
}初步認識了 defer 延遲函數(shù)的使用情況后,我們再結(jié)合文檔詳細解讀一下相關(guān)定義.
英文原版文檔
> A "defer" statement invokes a function whose execution is deferred tothe moment the surrounding function returns,either because the surrounding functionexecuted a returnstatement,reached the endof its function body,or because the corresponding goroutine is panicking.
中文翻譯文檔
> "defer"語句調(diào)用一個函數(shù),該函數(shù)的執(zhí)行被推遲到周圍函數(shù)返回的那一刻,這是因為周圍函數(shù)執(zhí)行了一個return語句,到達了函數(shù)體的末尾,或者是因為相應(yīng)的協(xié)程正在驚慌.
具體來說,延遲函數(shù)的執(zhí)行時機大概分為三種情況:
> because the surrounding functionexecuted a returnstatement
return 后面的 t.Log(4) 語句自然是不會運行的,程序最終輸出結(jié)果為 3 2 1 說明了 defer 語句會在周圍函數(shù)執(zhí)行 return 前依次逆序執(zhí)行.
func funcWithMultipleDeferAndReturn() {
defer fmt.Println(1)
defer fmt.Println(2)
fmt.Println(3)
return
fmt.Println(4)
}
func TestFuncWithMultipleDeferAndReturn(t *testing.T) {
// 「雪之夢技術(shù)驛站」: defer 延遲函數(shù)會在包圍函數(shù)正常return之前逆序執(zhí)行.
t.Log(" 「雪之夢技術(shù)驛站」: defer 延遲函數(shù)會在包圍函數(shù)正常return之前逆序執(zhí)行.")
// 3 2 1
funcWithMultipleDeferAndReturn()
}> reached the endof its function body
周圍函數(shù)的函數(shù)體運行到結(jié)尾前逆序執(zhí)行多個 defer 語句,即先輸出 3 后依次輸出 2 1. 最終函數(shù)的輸出結(jié)果是 3 2 1 ,也就說是沒有 return 聲明也能保證結(jié)束前執(zhí)行完 defer 延遲函數(shù).
func funcWithMultipleDeferAndEnd() {
defer fmt.Println(1)
defer fmt.Println(2)
fmt.Println(3)
}
func TestFuncWithMultipleDeferAndEnd(t *testing.T) {
// 「雪之夢技術(shù)驛站」: defer 延遲函數(shù)會在包圍函數(shù)到達函數(shù)體結(jié)尾之前逆序執(zhí)行.
t.Log(" 「雪之夢技術(shù)驛站」: defer 延遲函數(shù)會在包圍函數(shù)到達函數(shù)體結(jié)尾之前逆序執(zhí)行.")
// 3 2 1
funcWithMultipleDeferAndEnd()
}> because the corresponding goroutine is panicking
周圍函數(shù)萬一發(fā)生 panic 時也會先運行前面已經(jīng)定義好的 defer 語句,而 panic 后續(xù)代碼因為沒有特殊處理,所以程序崩潰了也就無法運行.
函數(shù)的最終輸出結(jié)果是 3 2 1 panic ,如此看來 defer 延遲函數(shù)還是非常盡忠職守的,雖然心里很慌但還是能保證老弱病殘先行撤退!
func funcWithMultipleDeferAndPanic() {
defer fmt.Println(1)
defer fmt.Println(2)
fmt.Println(3)
panic("panic")
fmt.Println(4)
}
func TestFuncWithMultipleDeferAndPanic(t *testing.T) {
// 「雪之夢技術(shù)驛站」: defer 延遲函數(shù)會在包圍函數(shù)panic驚慌失措之前逆序執(zhí)行.
t.Log(" 「雪之夢技術(shù)驛站」: defer 延遲函數(shù)會在包圍函數(shù)panic驚慌失措之前逆序執(zhí)行.")
// 3 2 1
funcWithMultipleDeferAndPanic()
}通過解讀 defer 延遲函數(shù)的定義以及相關(guān)示例,相信已經(jīng)講清楚什么是 defer 延遲函數(shù)了吧?
簡單地說,延遲函數(shù)就是一種未雨綢繆的規(guī)劃機制,幫助開發(fā)者編程程序時及時做好收尾善后工作,提前做好預(yù)案以準備隨時應(yīng)對各種情況.
當周圍函數(shù)正常執(zhí)行到到達函數(shù)體結(jié)尾時,如果發(fā)現(xiàn)存在延遲函數(shù)自然會逆序執(zhí)行延遲函數(shù).
當周圍函數(shù)正常執(zhí)行遇到return語句準備返回給調(diào)用者時,存在延遲函數(shù)時也會執(zhí)行,同樣滿足善后清理的需求.
當周圍函數(shù)異常運行不小心 panic 驚慌失措時,程序存在延遲函數(shù)也不會忘記執(zhí)行,提前做好預(yù)案發(fā)揮了作用.
所以不論是正常運行還是異常運行,提前做好預(yù)案總是沒錯的,基本上可以保證萬無一失,所以不妨考慮考慮 defer 延遲函數(shù)?

基本上成雙成對的操作都可以使用延遲函數(shù),尤其是申請的資源前后存在依賴關(guān)系時更應(yīng)該使用 defer 關(guān)鍵字來簡化處理邏輯.
下面舉兩個常見例子來說明延遲函數(shù)的應(yīng)用場景.
Open/Close
文件操作一般會涉及到打開和開閉操作,尤其是文件之間拷貝操作更是有著嚴格的順序,只需要按照申請資源的順序緊跟著defer 就可以滿足資源釋放操作.
func readFileWithDefer(filename string) ([]byte, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
return ioutil.ReadAll(f)
}Lock/Unlock
鎖的申請和釋放是保證同步的一種重要機制,需要申請多個鎖資源時可能存在依賴關(guān)系,不妨嘗試一下延遲函數(shù)!
var mu sync.Mutex
var m = make(map[string]int)
func lookupWithDefer(key string) int {
mu.Lock()
defer mu.Unlock()
return m[key]
}defer 延遲函數(shù)是保障關(guān)鍵邏輯正常運行的一種機制,如果存在多個延遲函數(shù)的話,一般會按照逆序的順序運行,類似于棧結(jié)構(gòu).
延遲函數(shù)的運行時機一般有三種情況:
周圍函數(shù)遇到返回時
func funcWithMultipleDeferAndReturn() {
defer fmt.Println(1)
defer fmt.Println(2)
fmt.Println(3)
return
fmt.Println(4)
}周圍函數(shù)函數(shù)體結(jié)尾處
func funcWithMultipleDeferAndEnd() {
defer fmt.Println(1)
defer fmt.Println(2)
fmt.Println(3)
}當前協(xié)程驚慌失措中
func funcWithMultipleDeferAndPanic() {
defer fmt.Println(1)
defer fmt.Println(2)
fmt.Println(3)
panic("panic")
fmt.Println(4)
}本文主要介紹了什么是 defer 延遲函數(shù),通過解讀官方文檔并配套相關(guān)代碼認識了延遲函數(shù),但是延遲函數(shù)中存在一些可能令人比較迷惑的地方.

讀者不妨看一下下面的代碼,將心里的猜想和實際運行結(jié)果比較一下,我們下次再接著分享,感謝你的閱讀.
func deferFuncWithAnonymousReturnValue() int {
var retVal int
defer func() {
retVal++
}()
return 0
}
func deferFuncWithNamedReturnValue() (retVal int) {
defer func() {
retVal++
}()
return 0
}“go語言中的defer延遲函數(shù)是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!
文章題目:go語言中的defer延遲函數(shù)是什么
網(wǎng)址分享:http://www.chinadenli.net/article8/iiegop.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站維護、網(wǎng)站設(shè)計公司、網(wǎng)站策劃、網(wǎng)站導(dǎo)航、用戶體驗、面包屑導(dǎo)航
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)