Golang異常處理之defer,panic,recover的使用詳解

延遲是什麼

defer即延遲語句,極個別的情況下,Go才使⽤defer、panic、recover這種異常處理形式。

defer可以延遲函數、延遲⽅法、延遲參數。

延遲函數

可以在函數中添加多個defer語句。

當函數執⾏到最後時,這些defer語句會按照逆序執⾏,最後該函數返回。特別是當你在進⾏⼀些打開資源的操作時,遇到錯誤需要提前返回,在返回前你需要關閉

相應的資源,不然很容易造成資源泄露等問題

如果有很多調⽤defer,那麼defer是采⽤後進先出模式

在離開所在的⽅法時,執⾏(報錯的時候也會執⾏)

示例代碼1:

package main
import "fmt"
func main() {
 defer funA()
 funB()
 funC()
 fmt.Println("main...over....") }
func funA() {
 fmt.Println("我是funA()...") }
func funB() { //
 fmt.Println("我是funB()...") }
func funC() {
 fmt.Println("我是funC()。。") }

運⾏結果:

我是funB()…
我是funC()。。
main…over….
我是funA()…

示例代碼2:

package main
import "fmt"
func main() {
 s1 := []int{78, 109, 2, 563, 300}
 largest(s1) }
func finished() {
 fmt.Println("結束!") }
func largest(s []int) {
 defer finished()
 fmt.Println("開始尋找最⼤數...")
 max := s[0]
 for _, v := range s {
 if v > max {
 max = v
 }
 }
 fmt.Printf("%v中的最⼤數為:%v \n", s , max) }

運⾏結果:

開始尋找最⼤數…
[78 109 2 563 300]中的最⼤數為:563
結束!

延遲⽅法

延遲並不僅僅局限於函數。延遲⼀個⽅法調⽤也是完全合法的。

示例代碼:

package main
import "fmt"
type person struct {
 firstName string
 lastName string
}
func (p person) fullName() {
 fmt.Printf("%s %s", p.firstName, p.lastName) }
func main() {
 p := person{"Steven", "Wang"}
 defer p.fullName()
 fmt.Printf("Welcome ") }

運⾏結果:

Welcome Steven Wang

延遲參數

延遲函數的參數在執⾏延遲語句時被執⾏,⽽不是在執⾏實際的函數調⽤時執⾏。

示例代碼:

package main
import "fmt"
func printAdd(a , b int) {
 fmt.Printf("延遲函數中:參數a , b分別為%d,%d ,兩數之和為:%d\n",
a , b , a+b) }
func main() {
 a := 5
 b := 6
 //延遲函數的參數在執⾏延遲語句時被執⾏,⽽不是在執⾏實際的函數調
⽤時執⾏。
 defer printAdd(a , b)
 a = 10
 b = 7
 fmt.Printf("延遲函數執⾏前:參數a , b分別為%d,%d ,兩數之和為:
%d\n", a , b , a+b) }

運⾏結果:

延遲函數執⾏前:參數a , b分別為10,7 ,兩數之和為:17
延遲函數中:參數a , b分別為5,6 ,兩數之和為:11

堆棧的推遲

當⼀個函數有多個延遲調⽤時,它們被添加到⼀個堆棧中,並在Last In First Out(LIFO)後進先出的順序中執⾏。

示例代碼:利⽤defer實現字符串倒序

package main
import "fmt"
func main() {
 name := "StevenWang歡迎學習區塊鏈"
 fmt.Printf("原始字符串: %s\n", name)
 fmt.Println("翻轉後字符串: ")
 ReverseString(name) }
func ReverseString(str string) {
 for _, v := range []rune(str) {
 defer fmt.Printf("%c", v)
 }
}

返回結果:

原始字符串: StevenWang歡迎學習區塊鏈
翻轉後字符串:
鏈塊區習學迎歡gnaWnevetS

延遲的應⽤

到⽬前為⽌,我們所寫的示例代碼,並沒有實際的應⽤。現在看⼀下關於延遲的應⽤。在不考慮代碼流的情況下,延遲被執⾏。讓我們以⼀個使⽤WaitGroup的程序示例來理解這個問題。我們將⾸先編寫程序⽽不使⽤延遲,然後我們將修改它以使⽤延遲,並理解延遲是多麼有⽤。

示例代碼:

package main
import (
 "fmt"
 "sync"
)
type rect struct {
 length int
 width int
}
func (r rect) area(wg *sync.WaitGroup) {
 if r.length < 0 {
 fmt.Printf("rect %v's length should be greater than zero\n", r)
 wg.Done()
 return
 }
 if r.width < 0 {
 fmt.Printf("rect %v's width should be greater than zero\n", r)
 wg.Done()
 return
 }
 area := r.length * r.width
 fmt.Printf("rect %v's area %d\n", r, area)
 wg.Done()
}
func main() {
 var wg sync.WaitGroup
 r1 := rect{-67, 89}
 r2 := rect{5, -67}
 r3 := rect{8, 9}
 rects := []rect{r1, r2, r3}
 for _, v := range rects {
 wg.Add(1)
 go v.area(&wg)
 }
 wg.Wait()
 fmt.Println("All go routines finished executing")
}

修改以上代碼:

package main
import (
 "fmt"
 "sync"
)
type rect struct {
 length int
 width int
}
func (r rect) area(wg *sync.WaitGroup) {
 defer wg.Done()
 if r.length < 0 {
 fmt.Printf("rect %v's length should be greater than zero\n", r)
 return
 }
 if r.width < 0 {
 fmt.Printf("rect %v's width should be greater than zero\n", r)
 return
 }
 area := r.length * r.width
 fmt.Printf("rect %v's area %d\n", r, area) }
func main() {
 var wg sync.WaitGroup
 r1 := rect{-67, 89}
 r2 := rect{5, -67}
 r3 := rect{8, 9}
 rects := []rect{r1, r2, r3}
 for _, v := range rects {
 wg.Add(1)
 go v.area(&wg)
 }
 wg.Wait()
 fmt.Println("All go routines finished executing")
}

程序運⾏結果:

rect {8 9}'s area 72
rect {-67 89}'s length should be greater than zero
rect {5 -67}'s width should be greater than zero
All go routines finished executing

panic和recover(宕機和宕機恢復)

panic和recover機制

1.概述:

panic:詞義"恐慌",recover:“恢復”

Go語⾔追求簡潔優雅,Go沒有像Java那樣的 try…catch…finally 異常處理機制。Go語⾔設計者認為,將異常與流程控制混在⼀起會讓代碼變得混亂。

Go語⾔中,使⽤多值返回來返回錯誤。不⽤異常代替錯誤,更不⽤異常來控制流程。

go語⾔利⽤panic(),recover(),實現程序中的極特殊的異常處理。換句話說,極個別的情況下,Go才使⽤defer、panic、recover這種異常處理形式。

  • panic(),讓當前的程序進⼊恐慌,中斷程序的執⾏。或者說,panic是⼀個內建函數,可以中斷原有的控制流程,進⼊⼀個令⼈恐慌的流程中。
  • 當函數F調⽤panic,函數F的執⾏被中斷,但是F中的延遲函數會正常執⾏,然後F返回到調⽤它的地⽅。在調⽤的地⽅,F的⾏為就像調⽤瞭panic。這⼀過程繼續向上,直到發⽣panic的goroutine中所有調⽤的函數返回,此時程序退出。
  • 恐慌可以直接調⽤panic產⽣。也可以由運⾏時錯誤產⽣,例如訪問越界的數組。
  • recover 是⼀個內建的函數,可以讓進⼊令⼈恐慌的流程中的goroutine恢復過來。
  • recover(),讓程序恢復,必須在defer函數中執⾏。換句話說,recover僅在延遲函數中有效。
  • 在正常 的執⾏過程中,調⽤recover會返回nil,並且沒有其它任何效果。如果當前的goroutine陷⼊恐慌,調⽤ recover可以捕獲到panic的輸⼊值,並且恢復正常的執⾏。

⼀定要記住,應當把它作為最後的⼿段來使⽤,也就是說,我們的代碼中應當沒有,或者很少有panic這樣的東⻄。

示例代碼

package main
import "fmt"
func main() {
 /*
 panic:詞義"恐慌",
 recover:"恢復"
 go語⾔利⽤panic(),recover(),實現程序中的極特殊的異常的處理
 panic(),讓當前的程序進⼊恐慌,中斷程序的執⾏
 recover(),讓程序恢復,必須在defer函數中執⾏
 */
 funA()
 funB()
 funC()
 fmt.Println("main...over....") }
func funA() {
 fmt.Println("我是函數funA()...") }
func funB() { //外圍函數
 defer func() {
 if msg := recover(); msg != nil {
 fmt.Println(msg, "恢復啦。。。")
 }
 }()
 fmt.Println("我是函數funB()...")
 for i := 1; i <= 10; i++ {
 fmt.Println("i:", i)
 if i == 5 {
 //讓程序中斷
 panic("funB函數,恐慌啦。。。") //打斷程序的執⾏。。
 }
 }
 //當外圍函數中的代碼引發運⾏恐慌時,隻有其中所有的延遲函數都執⾏完畢後,該運
⾏時恐慌才會真正被擴展⾄調⽤函數。
}
func funC() {
 defer func() {
 fmt.Println("func的延遲函數。。。")
 //if msg := recover(); msg != nil {
 // fmt.Println(msg, "恢復啦。。。")
 //}
 fmt.Println("recover執⾏瞭" , recover())
 }()
 fmt.Println("我是函數funC()。。")
 panic("funC恐慌啦。。") }

以上就是Golang異常處理之defer,panic,recover的使用詳解的詳細內容,更多關於Golang defer panic recover的資料請關註WalkonNet其它相關文章!

推薦閱讀: