Go基礎教程系列之defer、panic和recover詳解_第1頁
Go基礎教程系列之defer、panic和recover詳解_第2頁
Go基礎教程系列之defer、panic和recover詳解_第3頁
Go基礎教程系列之defer、panic和recover詳解_第4頁
Go基礎教程系列之defer、panic和recover詳解_第5頁
已閱讀5頁,還剩5頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領

文檔簡介

第Go基礎教程系列之defer、panic和recover詳解defer關鍵字

defer關鍵字可以讓函數(shù)或語句延遲到函數(shù)語句塊的最結尾時,即即將退出函數(shù)時執(zhí)行,即便函數(shù)中途報錯結束、即便已經(jīng)panic()、即便函數(shù)已經(jīng)return了,也都會執(zhí)行defer所推遲的對象。

其實defer的本質(zhì)是,當在某個函數(shù)中使用了defer關鍵字,則創(chuàng)建一個獨立的defer棧幀,并將該defer語句壓入棧中,同時將其使用的相關變量也拷貝到該棧幀中(顯然是按值拷貝的)。因為棧是LIFO方式,所以先壓棧的后執(zhí)行。因為是獨立的棧幀,所以即使調(diào)用者函數(shù)已經(jīng)返回或報錯,也一樣能在它們之后進入defer棧幀去執(zhí)行。

例如:

funcmain(){

funca(){

println("ina")

deferb()//將b()壓入defer棧中

println("leavinga")

//到了這里才會執(zhí)行b()

funcb(){

println("inb")

println("leavingb")

上面將輸出:

ina

leavinga

leavingb

即便是函數(shù)已經(jīng)報錯,或函數(shù)已經(jīng)return返回,defer的對象也會在函數(shù)退出前的最后一刻執(zhí)行。

funca()TYPE{

...CODE...

deferb()

...CODE...

//函數(shù)執(zhí)行出了錯誤

returnargs

//函數(shù)b()都會在這里執(zhí)行

但注意,由于Go的作用域采用的是詞法作用域,defer的定義位置決定了它推遲對象能看見的變量值,而不是推遲對象被調(diào)用時所能看見的值。

例如:

packagemain

varx=10

funcmain(){

funca(){

println("starta:",x)//輸出10

x=20

deferb(x)//壓棧,并按值拷貝20到棧中

x=30

println("leavinga:",x)//輸出30

//調(diào)用defer延遲的對象b(),輸出20

funcb(xint){

println("startb:",x)

比較下面的defer:

packagemain

varx=10

funcmain(){

funca()int{

println("starta:",x)//輸出10

x=20

deferfunc(){//壓棧,但并未傳值,所以內(nèi)部引用x

println("indefer:",x)//輸出30

x=30

println("leavinga:",x)//輸出30

returnx

上面defer推遲的匿名函數(shù)輸出的值是30,它看見的不應該是20嗎?先再改成下面的:

packagemain

varx=10

funcmain(){

funca()int{

println("starta:",x)//輸出10

x=20

deferfunc(xint){

println("indefer:",x)//輸出20

}(x)

x=30

println("leavinga:",x)//輸出30

returnx

這個defer推遲的對象中看見的卻是20,這和第一種deferb(x)是相同的。

原因在于defer推遲的如果是函數(shù),它直接就在它的定義位置處評估好參數(shù)、變量。該拷貝傳值的拷貝傳值,該指針相見的指針相見。所以,對于第(1)和第(3)種情況,在defer的定義位置處,就將x=20拷貝給了推遲的函數(shù)參數(shù),所以函數(shù)內(nèi)部操作的一直是x的副本。而第二種情況則是直接指向它所看見的x=20那個變量,則個變量是全局變量,當執(zhí)行x=30的時候會將其值修改,到執(zhí)行defer推遲的對象時,它指向的x的值已經(jīng)是修改過的。

再看下面這個例子,將defer放進一個語句塊中,并在這個語句塊中新聲明一個同名變量x:

funca()int{

println("starta:",x)//輸出10

x=20

x:=40

deferfunc(){

println("indefer:",x)//輸出40

x=30

println("leavinga:",x)//輸出30

returnx

上面的defer定義在語句塊中,它能看見的x是語句塊中x=40,它的x指向的是語句塊中的x。另一方面,當語句塊結束時,x=40的x會消失,但由于defer的函數(shù)中仍有x指向40這個值,所以40這個值仍被defer的函數(shù)引用著,它直到defer執(zhí)行完之后才會被GC回收。所以defer的函數(shù)在執(zhí)行的時候,仍然會輸出40。

如果語句塊內(nèi)有多個defer,則defer的對象以LIFO(lastinfirstout)的方式執(zhí)行,也就是說,先定義的defer后執(zhí)行。

funcmain(){

println("start...")

deferprintln("1")

deferprintln("2")

deferprintln("3")

deferprintln("4")

println("end...")

將輸出:

start...

end...

defer有什么用呢?一般用來做善后操作,例如清理垃圾、釋放資源,無論是否報錯都執(zhí)行defer對象。另一方面,defer可以讓這些善后操作的語句和開始語句放在一起,無論在可讀性上還是安全性上都很有改善,畢竟寫完開始語句就可以直接寫defer語句,永遠也不會忘記關閉、善后等操作。

例如,打開文件,關閉文件的操作寫在一起:

open()

deferfile.Close()

...操作文件...

以下是defer的一些常用場景:

打開關閉文件鎖定、釋放鎖建立連接、釋放連接作為結尾輸出結尾信息清理垃圾(如臨時文件)

panic()和recover()

panic()用于產(chǎn)生錯誤信息并終止當前的goroutine,一般將其看作是退出panic()所在函數(shù)以及退出調(diào)用panic()所在函數(shù)的函數(shù)。例如,G()中調(diào)用F(),F(xiàn)()中調(diào)用panic(),則F()退出,G()也退出。

注意,defer關鍵字推遲的對象是函數(shù)最后調(diào)用的,即使出現(xiàn)了panic也會調(diào)用defer推遲的對象。

例如,下面的代碼中,main()中輸出一個startmain之后調(diào)用a(),它會輸出starta,然后就panic了,panic()會輸出panic:panicina,然后報錯,終止程序。

funcmain(){

println("startmain")

println("endmain")

funca(){

println("starta")

panic("panicina")

println("enda")

執(zhí)行結果如下:

startmain

starta

panic:panicina

goroutine1[running]:

main.a()

E:/learning/err.go:14+0x63

main.main()

E:/learning/err.go:8+0x4c

exitstatus2

注意上面的enda和endmain都沒有被輸出。

可以使用recover()去捕獲panic()并恢復執(zhí)行。recover()用于捕捉panic()錯誤,并返回這個錯誤信息。但注意,即使recover()捕獲到了panic(),但調(diào)用含有panic()函數(shù)的函數(shù)(即上面的G()函數(shù))也會退出,所以如果recover()定義在G()中,則G()中調(diào)用F()函數(shù)之后的代碼都不會執(zhí)行(見下面的通用格式)。

以下是比較通用的panic()和recover()的格式:

funcmain(){

//下面的代碼會執(zhí)行

...CODEINMAIN...

funcG(){

deferfunc(){

ifstr:=recover();str!=nil{

fmt.Println(str)

...CODEING()...

//F()的調(diào)用必須在defer關鍵字之后

//該函數(shù)內(nèi)下面的代碼不會執(zhí)行

...CODEING()...

funcF(){

...CODE1...

panic("errorfound")

//下面的代碼不會執(zhí)行

...CODEINF()...

可以使用recover()去捕獲panic()并恢復執(zhí)行。但以下代碼是錯誤的:

funcmain(){

println("startmain")

println("endmain")

funca(){

println("starta")

panic("panicina")

//直接放在panic后是錯誤的

panic_str:=recover()

println(panic_str)

println("enda")

之所以錯誤,是因為panic()一出現(xiàn)就直接退出函數(shù)a()和main()了。要想recover()真正捕獲panic(),需要將recover()放在defer的推遲對象中,且defer的定義必須在panic()發(fā)生之前。

例如,下面是通用格式的示例:

packagemain

import"fmt"

funcmain(){

println("startmain")

println("endmain")

funca(){

println("starta")

panic("panicina")

println("enda")

funcb(){

println("startb")

deferfunc(){

ifstr:=recover();str!=nil{

fmt.Println(str)

println("endb")

以下是輸出結果:

startmain

startb

starta

panicina

endmain

注意上面的endb、enda都沒有被輸出,但是endmain輸出了。

panic()是內(nèi)置的函數(shù)(

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論