3 回答

TA貢獻1936條經驗 獲得超7個贊
無論如何都會panic
終止進程,因為沒有人從中恢復。如果你想在一個 goroutine 中從 panic 中恢復,你必須將recover
調用堆棧包裝在同一個 goroutine 中。
wg.Done
在這種情況下,將由defer
聲明調用。但是這個過程可能會在主 goroutine 完成之前結束wg.Wait
。

TA貢獻1869條經驗 獲得超4個贊
參考src/builtin/builtin.go
panic 內置函數停止當前 goroutine 的正常執行。當函數 F 調用 panic 時,F 的正常執行會立即停止。任何被 F 延遲執行的函數都以通常的方式運行,然后 F 返回給它的調用者。對于調用者 G,調用 F 就像調用 panic,終止 G 的執行并運行任何延遲函數。這一直持續到執行 goroutine 中的所有函數都以相反的順序停止。此時,程序終止并報告錯誤情況,包括 panic 的參數值。這種終止序列稱為 panicing,可以通過內置函數 recover 來控制。
之后panic
,defer
將調用 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
那么你的第二個問題,“如何防止這種情況發生?”
如前所述,您可以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貢獻1829條經驗 獲得超7個贊
可以通過添加來引發不需要的行為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
該問題的解決方案是不延遲調用Done()并將其放在最后:
func main() {
? ? wg := &sync.WaitGroup{}
? ? wg.Add(1)
? ? go func() {
? ? ? ? // Long running task which might panic
? ? ? ? wg.Done()
? ? }()
? ? wg.Wait()
}
在上面的代碼中,不存在對 go routine 行為的實現細節的依賴,并且在發生 panic 時總是會失敗。
- 3 回答
- 0 關注
- 172 瀏覽
添加回答
舉報