Mengapa saya hanya mendapat beberapa ralat dan bukan semua ralat daripada goroutine yang saya mulakan?

WBOY
Lepaskan: 2024-02-09 14:40:20
ke hadapan
1201 orang telah melayarinya

为什么我只收到部分错误,而不是我启动的 goroutine 中的所有错误?

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.

Kandungan soalan

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)
    }
}
Salin selepas log masuk

Akhirnya t.log(err) mengeluarkan err1 atau err2, tetapi saya mahu ia mengeluarkan err1 err2. Mengapa ia terlepas ralat. t.log(err)输出err1err2,但我希望它输出err1 err2。为什么它会漏掉一个错误。

解决方法

这是因为 (*cycle).waitalldone 不会等待收集错误的 goroutine 完成。如果您使用 -race

Penyelesaian

Ini kerana (*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
Salin selepas log masuk
rangePerubahan 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
  }
Salin selepas log masuk
🎜Dan gelung for boleh dipermudahkan menggunakan klausa 🎜: 🎜
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
}
Salin selepas log masuk

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!

Label berkaitan:
sumber:stackoverflow.com
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!