這篇文章主要介紹了Go語言中panic函數(shù)、recover函數(shù)以及defer語句怎么用,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
成都創(chuàng)新互聯(lián)公司,為您提供成都網(wǎng)站建設(shè)、重慶網(wǎng)站制作、網(wǎng)站營銷推廣、網(wǎng)站開發(fā)設(shè)計,對服務(wù)格柵板等多個行業(yè)擁有豐富的網(wǎng)站建設(shè)及推廣經(jīng)驗。成都創(chuàng)新互聯(lián)公司網(wǎng)站建設(shè)公司成立于2013年,提供專業(yè)網(wǎng)站制作報價服務(wù),我們深知市場的競爭激烈,認(rèn)真對待每位客戶,為客戶提供賞心悅目的作品。 與客戶共同發(fā)展進(jìn)步,是我們永遠(yuǎn)的責(zé)任!panic,Go語言的另外一種錯誤處理方式。嚴(yán)格來講,它處理的不是錯誤,而是異常,并且是一種在我們意料之外的程序異常。
要分析panic詳情,首先來生成一個panic。比如在一個切片里,它的長度是5,但是要通過索引5訪問其中的元素,這樣的訪問是不正確的。比如下面這樣:
func main() { l := []int{1, 2, 3, 4, 5} _ = l[5] }
程序在運行時,會在執(zhí)行到這行代碼的時候拋出panic,提示用戶索引越界了。這不僅僅是個提示。當(dāng)panic被拋出后,如果沒有在程序里添加任何保護(hù)措施的話,程序(或者說代表它的那個進(jìn)程)會在打印出pinic的詳細(xì)情況之后終止運行。下面是panic的詳情:
panic: runtime error: index out of range goroutine 1 [running]: main.main() H:/Go/src/Go36/article21/example01/main.go:5 +0x3d exit status 2
先看第一行的內(nèi)容,其中的“runtime error”表示,這是一個runtime代碼包中拋出的panic。在這個panic中,包含一個runtime.Error接口類型的值。runtime.Error接口內(nèi)嵌了error接口并做了一點擴展,runtime包中有不少它的實現(xiàn)類型。實際上,在"panic:"右邊的內(nèi)容,就是這個panic包含的runtime.Error類型值的字符串表示形式。
此外,panic詳情中一本還會包含與引發(fā)原因有關(guān)的goroutine的代碼執(zhí)行信息。這里的“goroutine 1 [running]:”表示是一個ID為1的goroutine在這個panic被引發(fā)的時候正在運行。
再看下一行,“main.main()”表明了這個goroutine包裝的go函數(shù)就是命令源碼文件里的main函數(shù)。再往下一行,指出了這個源碼文件的絕對路徑,以及代碼在文件中所處的行。這一行的最后的+0x3d代表的是:此行代碼相對于其所屬函數(shù)的入口程序計數(shù)偏移量。不過,一般這個對我們沒什么用。
最后的“exit status 2”,是這個程序退出的狀態(tài)碼。在大多數(shù)操作系統(tǒng)中,只要退出狀態(tài)碼不是0,就是非正常結(jié)束。在Go語言中,因panic導(dǎo)致的程序結(jié)束運行的退出狀態(tài)碼一般都會是2。
先說一個大致的過程:當(dāng)引發(fā)了一個panic。
這時,初始的panic詳情會被建立起來,并且該程序的控制權(quán)會立即從此代碼行轉(zhuǎn)移至調(diào)用起所屬函數(shù)的那行代碼上,也就是調(diào)用棧中的上一級。這樣就意味著,此行代碼所屬的函數(shù)的執(zhí)行立即終止。
緊接著,控制權(quán)并不會停在當(dāng)前位置,它又會立即轉(zhuǎn)移至再上一級的調(diào)用代碼處??刂茩?quán)如此一級一級地沿著調(diào)用棧的方向轉(zhuǎn)播至頂端,就是我們編寫的最外層的函數(shù)那里。這個最外層的函數(shù)就是go函數(shù),對于主goroutine來說就是main函數(shù)。但是控制權(quán)到這還不會停止轉(zhuǎn)移,而是被Go言語運行時的系統(tǒng)回收。
隨后,程序崩潰并終止運行,運行這次程序的進(jìn)程也會隨之死亡并消失。而在這個控制權(quán)傳播的過程中,panic詳情會被逐漸地積累和完善,并會在程序終止之前被打印出來。
這里再補充一下,函數(shù)引發(fā)panic與函數(shù)返回錯誤值的意義是完全不同的
當(dāng)函數(shù)返回一個非nil的錯誤值時,函數(shù)的調(diào)用方有權(quán)選擇不處理,并且不處理的后果往往是不致命的。
當(dāng)一個panic發(fā)生時,如果不施加任何保護(hù)措施,那么導(dǎo)致的后果就是程序崩潰,這顯然是致命的。
下面的例子清楚地展示了上面描述的控制權(quán)一級一級向上傳播的過程:
package main import "fmt" func main() { fmt.Println("main Start") caller1() fmt.Println("main End") } func caller1() { fmt.Println("caller1 Start") caller2() fmt.Println("caller1 End") } func caller2() { fmt.Println("caller2 Start") l := []int{1, 2, 3, 4, 5} _ = l[5] fmt.Println("caller2 End") }
這里,panic詳情會在控制權(quán)傳播的過程中,被逐漸地積累和完善。并且,控制權(quán)會一級一級地沿著調(diào)用棧的反方向傳播至頂端。因此,針對某個goroutine的代碼執(zhí)行信息中,調(diào)用棧底端的信息會先出現(xiàn),然后是上一級調(diào)用的信息。以此類推,最后才是此調(diào)用棧頂端的信息。
所以是,main函數(shù)調(diào)用了caller1函數(shù),而caller1函數(shù)又調(diào)用了caller2函數(shù)。那么caller2函數(shù)中代碼執(zhí)行的信息會先出現(xiàn),然后是caller1函數(shù)中代碼的執(zhí)行的信息,最后才是main函數(shù)的信息:
PS H:\Go\src\Go36\article21\example02> go run main.go main Start caller1 Start caller2 Start panic: runtime error: index out of range goroutine 1 [running]: main.caller2() H:/Go/src/Go36/article21/example02/main.go:20 +0xa2 main.caller1() H:/Go/src/Go36/article21/example02/main.go:13 +0x77 main.main() H:/Go/src/Go36/article21/example02/main.go:7 +0x77 exit status 2 PS H:\Go\src\Go36\article21\example02>
到這里,應(yīng)該已經(jīng)對panic被引發(fā)后的程序終止的過程有一定的了解了。深入了解這個過程以及正確的解讀panic詳情是一項必備技能。這在調(diào)試Go程序或為Go程序排查錯誤的時候非常有用。
如果一個panic是我們在無意間引發(fā)的,那么其中的值只能由Go語言運行時系統(tǒng)給定。但是,當(dāng)我們使用panic函數(shù)有意的引發(fā)一個panic的時候,就可以自行指定其包含的值。
在調(diào)用一個panic函數(shù)時,把某個值作為參數(shù)傳遞給函數(shù)就是可以了。panic函數(shù)只有一個參數(shù),并且類型是空接口,所以從語法上講,它可以接受任何類型的值。一旦程序異常了,就一定會把異常的相關(guān)信息記錄下來,所以就會需用輸出這個參數(shù)的字符串表示形式。雖然fmt.Sprintf和fmt.Fprintf這類可以格式化并輸出參數(shù)的函數(shù)也符合要求。不過,在功能上推薦使用自定義的Error方法或者String方法。因此,為部不同的數(shù)據(jù)類型分別編寫這兩種方法是選。這樣,在程序崩潰的時候,panic包含的拿著值的字符串表示形式就會被打印出來:
package main import ( "fmt" // "errors" ) func caller() { fmt.Println("caller Start") // panic(errors.New("Something Wrong")) // 正例 panic(fmt.Errorf("Something Wrong %s", "2")) // 正例 // panic(fmt.Println) // 反例 } func main() { fmt.Println("main Start") caller() fmt.Println("main End") }
可以施加應(yīng)對panic的保護(hù)措施,避免程序崩潰。Go語言的內(nèi)建函數(shù)recover專門用于恢復(fù)panic。recover無需任何參數(shù),并且會返回一個空接口類型的值。這個返回值,就是panic傳入的參數(shù)的副本。
下面先看一個錯誤的用法:
func main() { fmt.Println("main Start") panic(errors.New("Something Wrong")) p := reover() fmt.Println(p) fmt.Pringln("main End") }
這里,引發(fā)panic之后,想緊接著調(diào)用recover函數(shù)。但是,函數(shù)的執(zhí)行會在panic這行就終止了,這個recover函數(shù)的調(diào)用根本就沒有機會執(zhí)行。要想正確的調(diào)用recover函數(shù),需要用到defer語句。下面是修正過的代碼:
package main import ( "fmt" "errors" ) func main() { fmt.Println("main Start") defer func() { fmt.Println("defer Start") if p := recover(); p != nil { fmt.Printf("Panic: %s\n", p) } fmt.Println("defer End") }() panic(errors.New("Something Wrong")) fmt.Println("main End") }
在這個main函數(shù)中,先編寫了一條defer語句,并且再defer函數(shù)中調(diào)用了recover函數(shù)。僅當(dāng)調(diào)用的結(jié)果不為nil時,也就是panic確實已經(jīng)發(fā)生時,才會打印panic的內(nèi)容。這里要盡量把defer語句寫在函數(shù)體的開始處,因為引發(fā)panic語句之后的所有語句,都不會有任何執(zhí)行的機會。
defer語句就是被用來延遲執(zhí)行代碼的。延遲到該語句所在的函數(shù)即將執(zhí)行結(jié)束的那一刻,無論結(jié)束執(zhí)行的原因是什么(包括panic)。
與別的go語句類型,一個defer語句有一個defer關(guān)鍵字和一個調(diào)用表達(dá)式組成。這里存在一些限制,有一些調(diào)用表達(dá)式是不能出現(xiàn)在這里的:針對Go語句內(nèi)奸函數(shù)的調(diào)用表達(dá)式,以及針對unsafe包中的函數(shù)的調(diào)用表達(dá)式。在這里被調(diào)用函數(shù)可以是有名稱的,也可以是內(nèi)名的??梢赃@這里的函數(shù)叫做defer函數(shù)或者延遲函數(shù)。注意,被延遲執(zhí)行的是defer函數(shù),而不是defer語句。
defer執(zhí)行順序
如果一個函數(shù)中有多條defer語句的情況,那么defer函數(shù)的調(diào)用的執(zhí)行順序與它們所屬的defer語句的執(zhí)行順序完全相反。當(dāng)一個函數(shù)即將結(jié)束執(zhí)行時,其中寫在最下邊的defer函數(shù)調(diào)用會最新執(zhí)行,最上表的defer函數(shù)調(diào)用會最后一個執(zhí)行。
在defer語句每次執(zhí)行的時候,Go語言會把它所攜帶的defer函數(shù)及其參數(shù)值另行存儲到一個隊列中。這個隊列與該defer語句所屬的函數(shù)是對應(yīng)的,并且它是先進(jìn)后出(FILO)的,想到與一個棧。在需要執(zhí)行某個函數(shù)的defer函數(shù)調(diào)用的時候,Go語言會先拿到對應(yīng)的隊列,然后從該隊列中一個一個的取出defer函數(shù)及其參數(shù)值并逐個執(zhí)行調(diào)用。這就是實現(xiàn)這個執(zhí)行順序的原因了。
下面是一個簡單的示例,展示了defer的調(diào)用順序:
package main import "fmt" func main() { defer fmt.Println("first defer") for i := 0; i < 3; i++ { defer fmt.Printf("defer in for [%d]\n", i) } defer fmt.Println("last defer") }
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“Go語言中panic函數(shù)、recover函數(shù)以及defer語句怎么用”這篇文章對大家有幫助,同時也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,更多相關(guān)知識等著你來學(xué)習(xí)!
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機、免備案服務(wù)器”等云主機租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。
分享名稱:Go語言中panic函數(shù)、recover函數(shù)以及defer語句怎么用-創(chuàng)新互聯(lián)
鏈接地址:http://m.rwnh.cn/article24/dggece.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供關(guān)鍵詞優(yōu)化、ChatGPT、域名注冊、網(wǎng)站收錄、建站公司、網(wǎng)站設(shè)計
聲明:本網(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)