php小編蘋果為你解答:在Go語言中,當一個goroutine發生錯誤時,並不會自動傳播給主協程。相反,它會被靜默地忽略掉,這可能會導致你只收到部分錯誤而不是啟動的所有goroutine中的錯誤。這是因為Go語言設計的初衷是讓程式保持穩定和高效,即使在出現錯誤的情況下也不會立即停止整個程式。如果你想要捕捉所有的錯誤,你可以使用通道或其他機制來顯示地傳遞錯誤訊息。這樣你就可以確保所有的錯誤都被正確處理。
我定義了一個cycle類別來處理並發任務。我想要的是運行兩個函數,每個函數都在一個 goroutine 中,等待它們完成並將它們的輸出錯誤合併在一起。但我只收到一個錯誤。每個方法的職責如下:
run
-在goroutine中運行函數,並收集其錯誤
waitalldone
-將所有函數錯誤合併在一起並等待所有函數完成
do1、do2
- 測試函數
import ( "fmt" "go.uber.org/multierr" "sync" "testing" ) type Cycle struct { errChan chan error wg sync.WaitGroup } func NewCycle() *Cycle { return &Cycle{ errChan: make(chan error), wg: sync.WaitGroup{}, } } // run fn and collect its error into error channel func (c *Cycle) Run(fn func() error) { c.wg.Add(1) go func() { defer c.wg.Done() if err := fn(); err != nil { c.errChan <- err } }() } // wait all fn finish and combine their error together func (c *Cycle) WaitAllDone() error { var err error go func() { for { if tmpErr, ok := <-c.errChan; ok { err = multierr.Append(err, tmpErr) } else{ break } } }() c.wg.Wait() close(c.errChan) return err } func Do1() error { return fmt.Errorf("ERR1") } func Do2() error { return fmt.Errorf("ERR2") } func Test41(t *testing.T) { c := NewCycle() c.Run(Do1) c.Run(Do2) if err := c.WaitAllDone(); err != nil { t.Log(err) } }
最終t.log(err)
輸出err1
或err2
,但我希望它輸出err1 err2
。為什麼它會漏掉一個錯誤。
這是因為 (*cycle).waitalldone
不會等待收集錯誤的 goroutine 完成。如果您使用 -race
標誌來執行程式碼,有時它可能會報告幾個 data race 錯誤。這是其中之一:
$ go test -race . ================== warning: data race write at 0x00c0000a0610 by goroutine 10: m.(*cycle).waitalldone.func1() /home/zeke/src/temp/76370962/main_test.go:40 +0xb6 previous read at 0x00c0000a0610 by goroutine 7: m.(*cycle).waitalldone() /home/zeke/src/temp/76370962/main_test.go:48 +0x14e m.test41() /home/zeke/src/temp/76370962/main_test.go:63 +0xa4 testing.trunner() /snap/go/current/src/testing/testing.go:1576 +0x216 testing.(*t).run.func1() /snap/go/current/src/testing/testing.go:1629 +0x47
此變更將解決該問題:
func (c *cycle) waitalldone() error { var err error + done := make(chan int) go func() { for { if tmperr, ok := <-c.errchan; ok { err = multierr.append(err, tmperr) } else { break } } + close(done) }() c.wg.wait() close(c.errchan) + <-done return err }
並且可以使用 range
子句來簡化 for 迴圈:
func (c *Cycle) WaitAllDone() error { var err error done := make(chan int) go func() { for tmpErr := range c.errChan { err = multierr.Append(err, tmpErr) } close(done) }() c.wg.Wait() close(c.errChan) <-done return err }
以上是為什麼我只收到部分錯誤,而不是我啟動的 goroutine 中的所有錯誤?的詳細內容。更多資訊請關注PHP中文網其他相關文章!