PHP Editor Apple mempunyai jawapan untuk anda: Dalam bahasa Go, apabila ralat berlaku dalam goroutine, ia tidak akan disebarkan secara automatik ke coroutine utama. Sebaliknya, ia diabaikan secara senyap, yang mungkin menyebabkan anda hanya menerima ralat separa dan bukannya ralat dalam semua gorout yang dilancarkan. Ini kerana niat asal reka bentuk bahasa Go adalah untuk memastikan program stabil dan cekap, dan keseluruhan program tidak akan dihentikan serta-merta walaupun berlaku ralat. Jika anda ingin menangkap semua ralat, anda boleh menggunakan saluran atau mekanisme lain untuk menyampaikan maklumat ralat secara eksplisit. Dengan cara ini anda boleh memastikan bahawa semua ralat dikendalikan dengan betul.
Saya menentukan kelas kitaran untuk mengendalikan tugasan serentak. Apa yang saya mahu ialah menjalankan dua fungsi, masing-masing dalam goroutine, tunggu ia selesai dan gabungkan ralat keluaran mereka bersama-sama. Tetapi saya hanya mendapat ralat. Tanggungjawab setiap kaedah adalah seperti berikut:
run
- Jalankan fungsi dalam goroutine dan kumpulkan ralatnya
waitalldone
- Gabungkan semua ralat fungsi bersama-sama dan tunggu semua fungsi selesai
do1、do2
- fungsi ujian
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) } }
Akhirnya t.log(err)
mengeluarkan err1
atau err2
, tetapi saya mahu ia mengeluarkan err1 err2
. Mengapa ia terlepas ralat. t.log(err)
输出err1
或err2
,但我希望它输出err1 err2
。为什么它会漏掉一个错误。
这是因为 (*cycle).waitalldone
不会等待收集错误的 goroutine 完成。如果您使用 -race
(*cycle).waitalldone
tidak menunggu ralat pengumpulan goroutine selesai. Jika anda menjalankan kod anda dengan bendera -race
, kadangkala ia mungkin melaporkan beberapa ralat perlumbaan data. Ini antaranya:
$ 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
range
Perubahan ini akan menyelesaikan masalah:
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 }
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 }
Atas ialah kandungan terperinci Mengapa saya hanya mendapat beberapa ralat dan bukan semua ralat daripada goroutine yang saya mulakan?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!