Maison  >  Article  >  développement back-end  >  Pourquoi est-ce que je reçois seulement quelques erreurs et pas toutes les erreurs de la goroutine que j'ai démarrée ?

Pourquoi est-ce que je reçois seulement quelques erreurs et pas toutes les erreurs de la goroutine que j'ai démarrée ?

WBOY
WBOYavant
2024-02-09 14:40:201180parcourir

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

L'éditeur PHP Apple vous donnera la réponse : Dans le langage Go, lorsqu'une erreur survient dans une goroutine, elle ne sera pas automatiquement propagée à la coroutine principale. Au lieu de cela, il est ignoré en silence, ce qui peut entraîner la réception d'erreurs partielles plutôt que d'erreurs dans toutes les goroutines lancées. En effet, l'intention initiale de la conception du langage Go est de maintenir le programme stable et efficace, et l'ensemble du programme ne sera pas arrêté immédiatement, même en cas d'erreur. Si vous souhaitez détecter toutes les erreurs, vous pouvez utiliser un canal ou un autre mécanisme pour transmettre explicitement les informations sur les erreurs. De cette façon, vous pouvez vous assurer que toutes les erreurs sont traitées correctement.

Contenu de la question

J'ai défini une classe de cycle pour gérer les tâches simultanées. Ce que je veux, c'est exécuter deux fonctions, chacune dans une goroutine, attendre qu'elles se terminent et fusionner leurs erreurs de sortie. Mais je n'obtiens qu'une erreur. Les responsabilités de chaque méthode sont les suivantes :

run - Exécuter une fonction dans une goroutine et collecter ses erreurs

waitalldone - Fusionnez toutes les erreurs de fonction et attendez que toutes les fonctions soient terminées

do1、do2 - fonction test

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

En fin de compte, t.log(err) génère err1 ou err2, mais je veux qu'il génère err1 err2. Pourquoi manque-t-il une erreur. t.log(err)输出err1err2,但我希望它输出err1 err2。为什么它会漏掉一个错误。

解决方法

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

Solution de contournement

En effet, (*cycle).waitalldone n'attend pas la fin de la goroutine de collecte des erreurs. Si vous exécutez votre code avec l'indicateur -race, il peut parfois signaler plusieurs erreurs de course aux données. En voici un :

$ 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
rangeCe changement résoudra le problème :

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
  }
🎜Et la boucle for peut être simplifiée en utilisant la clause 🎜 : 🎜
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
}

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer