W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
恐慌和恢復(fù)(panic/recover)已經(jīng)在之前的文章中介紹過了。 下面將展示一些恐慌/恢復(fù)用例。
這可能是最常見的panic/recover用例了。 此用例廣泛地使用于并發(fā)程序中,尤其是響應(yīng)大量用戶請(qǐng)求的應(yīng)用。
一個(gè)例子:
package main
import "errors"
import "log"
import "net"
func main() {
listener, err := net.Listen("tcp", ":12345")
if err != nil {
log.Fatalln(err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Println(err)
}
// 在一個(gè)新協(xié)程中處理客戶端連接。
go ClientHandler(conn)
}
}
func ClientHandler(c net.Conn) {
defer func() {
if v := recover(); v != nil {
log.Println("捕獲了一個(gè)恐慌:", v)
log.Println("防止了程序崩潰")
}
c.Close()
}()
panic("未知錯(cuò)誤") // 演示目的產(chǎn)生的一個(gè)恐慌
}
運(yùn)行此服務(wù)器程序,并在另一個(gè)終端窗口運(yùn)行telnet localhost 12345
,我們可以觀察到服務(wù)器程序不會(huì)因?yàn)榭蛻暨B接處理協(xié)程中的產(chǎn)生的恐慌而導(dǎo)致崩潰。
如果我們?cè)谏侠胁徊东@客戶連接處理協(xié)程中的潛在恐慌,則這樣的恐慌將使整個(gè)程序崩潰。
當(dāng)在一個(gè)協(xié)程將要退出時(shí),程序偵測(cè)到此協(xié)程是因?yàn)橐粋€(gè)恐慌而導(dǎo)致此次退出時(shí),我們可以立即重新創(chuàng)建一個(gè)相同功能的協(xié)程。 一個(gè)例子:
package main
import "log"
import "time"
func shouldNotExit() {
for {
time.Sleep(time.Second) // 模擬一個(gè)工作負(fù)載
// 模擬一個(gè)未預(yù)料到的恐慌。
if time.Now().UnixNano() & 0x3 == 0 {
panic("unexpected situation")
}
}
}
func NeverExit(name string, f func()) {
defer func() {
if v := recover(); v != nil { // 偵測(cè)到一個(gè)恐慌
log.Printf("協(xié)程%s崩潰了,準(zhǔn)備重啟一個(gè)", name)
go NeverExit(name, f) // 重啟一個(gè)同功能協(xié)程
}
}()
f()
}
func main() {
log.SetFlags(0)
go NeverExit("job#A", shouldNotExit)
go NeverExit("job#B", shouldNotExit)
select{} // 永久阻塞主線程
}
有時(shí),我們可以使用panic
/recover
函數(shù)調(diào)用來模擬跨函數(shù)跳轉(zhuǎn),盡管一般這種方式并不推薦使用。 這種跳轉(zhuǎn)方式的可讀性不高,代碼效率也不是很高,唯一的好處是它有時(shí)可以使代碼看上去不是很啰嗦。
在下面這個(gè)例子中,一旦一個(gè)恐慌在一個(gè)內(nèi)嵌函數(shù)中產(chǎn)生,當(dāng)前協(xié)程中的執(zhí)行將會(huì)跳轉(zhuǎn)到延遲調(diào)用處。
package main
import "fmt"
func main() {
n := func () (result int) {
defer func() {
if v := recover(); v != nil {
if n, ok := v.(int); ok {
result = n
}
}
}()
func () {
func () {
func () {
// ...
panic(123) // 用恐慌來表示成功返回
}()
// ...
}()
}()
// ...
return 0
}()
fmt.Println(n) // 123
}
一個(gè)例子:
func doSomething() (err error) {
defer func() {
err = recover()
}()
doStep1()
doStep2()
doStep3()
doStep4()
doStep5()
return
}
// 在現(xiàn)實(shí)中,各個(gè)doStepN函數(shù)的原型可能不同。
// 每個(gè)doStepN函數(shù)的行為如下:
// * 如果已經(jīng)成功,則調(diào)用panic(nil)來制造一個(gè)恐慌
// 以示不需繼續(xù);
// * 如果本步失敗,則調(diào)用panic(err)來制造一個(gè)恐慌
// 以示不需繼續(xù);
// * 不制造任何恐慌表示繼續(xù)下一步。
func doStepN() {
...
if err != nil {
panic(err)
}
...
if done {
panic(nil)
}
}
下面這段同功能的代碼比上面這段代碼看上去要啰嗦一些。
func doSomething() (err error) {
shouldContinue, err := doStep1()
if !shouldContinue {
return err
}
shouldContinue, err = doStep2()
if !shouldContinue {
return err
}
shouldContinue, err = doStep3()
if !shouldContinue {
return err
}
shouldContinue, err = doStep4()
if !shouldContinue {
return err
}
shouldContinue, err = doStep5()
if !shouldContinue {
return err
}
return
}
// 如果返回值err不為nil,則shouldContinue一定為true。
// 如果shouldContinue為true,返回值err可能為nil或者非nil。
func doStepN() (shouldContinue bool, err error) {
...
if err != nil {
return false, err
}
...
if done {
return false, nil
}
return true, nil
}
但是,這種panic
/recover
函數(shù)調(diào)用的使用方式一般并不推薦使用,因?yàn)樗男事缘鸵恍⑶疫@種用法不太符合Go編程習(xí)俗。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: