Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Mengapa saya hanya mendapat beberapa ralat dan bukan semua ralat daripada goroutine yang saya mulakan?

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

WBOY
WBOYke hadapan
2024-02-09 14:40:201179semak imbas

为什么我只收到部分错误,而不是我启动的 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)
    }
}

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
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
  }
🎜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
}

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!

Kenyataan:
Artikel ini dikembalikan pada:stackoverflow.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam