3 回答

TA貢獻(xiàn)1936條經(jīng)驗(yàn) 獲得超7個贊
無論如何都會panic
終止進(jìn)程,因?yàn)闆]有人從中恢復(fù)。如果你想在一個 goroutine 中從 panic 中恢復(fù),你必須將recover
調(diào)用堆棧包裝在同一個 goroutine 中。
wg.Done
在這種情況下,將由defer
聲明調(diào)用。但是這個過程可能會在主 goroutine 完成之前結(jié)束wg.Wait
。

TA貢獻(xiàn)1869條經(jīng)驗(yàn) 獲得超4個贊
參考src/builtin/builtin.go
panic 內(nèi)置函數(shù)停止當(dāng)前 goroutine 的正常執(zhí)行。當(dāng)函數(shù) F 調(diào)用 panic 時,F(xiàn) 的正常執(zhí)行會立即停止。任何被 F 延遲執(zhí)行的函數(shù)都以通常的方式運(yùn)行,然后 F 返回給它的調(diào)用者。對于調(diào)用者 G,調(diào)用 F 就像調(diào)用 panic,終止 G 的執(zhí)行并運(yùn)行任何延遲函數(shù)。這一直持續(xù)到執(zhí)行 goroutine 中的所有函數(shù)都以相反的順序停止。此時,程序終止并報(bào)告錯誤情況,包括 panic 的參數(shù)值。這種終止序列稱為 panicing,可以通過內(nèi)置函數(shù) recover 來控制。
之后panic
,defer
將調(diào)用 func。
在操場上檢查一下:https ://play.golang.org/p/yrXkEbE1Af7
package main
import (
? ? "sync"
? ? "fmt"
)
func main() {
? ? wg := &sync.WaitGroup{}
? ? wg.Add(1)
? ? go func() {
? ? ? ? defer func(){
? ? ? ? ? ? fmt.Println("expected to be called after panic")
? ? ? ? ? ? wg.Done()
? ? ? ? }()
? ? ? ? // Long running task
? ? ? ? panic("Something unexpected happened.")
? ? }()
? ? wg.Wait()
}
輸出
expected to be called after panic
panic: Something unexpected happened.
goroutine 5 [running]:
main.main.func1(0x416020, 0x0)
? ? /tmp/sandbox946785562/main.go:17 +0x60
created by main.main
? ? /tmp/sandbox946785562/main.go:11 +0x80
那么你的第二個問題,“如何防止這種情況發(fā)生?”
如前所述,您可以recover在panic
游樂場:https://play.golang.org/p/76pPrCVYN8u
package main
import (
? ? "sync"
? ? "fmt"
)
func main() {
? ? wg := &sync.WaitGroup{}
? ? wg.Add(1)
? ? go func() {
? ? ? ? defer func(){
? ? ? ? ? ? if x:=recover();x!=nil{
? ? ? ? ? ? ? ? fmt.Printf("%+v\n",x)
? ? ? ? ? ? }
? ? ? ? ? ? wg.Done()
? ? ? ? }()
? ? ? ? // Long running task
? ? ? ? panic("Something unexpected happened.")
? ? }()
? ? wg.Wait()
? ? for i:=0;i<10;i++{
? ? ? ? fmt.Println(i)
? ? }
}
輸出
Something unexpected happened.
0
1
2
3
4
5
6
7
8
9

TA貢獻(xiàn)1829條經(jīng)驗(yàn) 獲得超7個贊
可以通過添加來引發(fā)不需要的行為defer time.Sleep(time.Second):
func main() {
? ? wg := &sync.WaitGroup{}
? ? wg.Add(1)
? ? go func() {
? ? ? ? defer time.Sleep(time.Second)
? ? ? ? defer wg.Done()
? ? ? ? // Long running task
? ? ? ? panic("Something unexpected happened.")
? ? }()
? ? wg.Wait()
}
D:\Projects\Code\Go\src\zyl\testexit>go build .
D:\Projects\Code\Go\src\zyl\testexit>.\testexit.exe
D:\Projects\Code\Go\src\zyl\testexit>echo %errorlevel%
0
該問題的解決方案是不延遲調(diào)用Done()并將其放在最后:
func main() {
? ? wg := &sync.WaitGroup{}
? ? wg.Add(1)
? ? go func() {
? ? ? ? // Long running task which might panic
? ? ? ? wg.Done()
? ? }()
? ? wg.Wait()
}
在上面的代碼中,不存在對 go routine 行為的實(shí)現(xiàn)細(xì)節(jié)的依賴,并且在發(fā)生 panic 時總是會失敗。
- 3 回答
- 0 關(guān)注
- 170 瀏覽
添加回答
舉報(bào)