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