从嵌套在循环中的 goroutines 中收集错误

我正在尝试从循环中的 goroutines 收集错误,但不明白它必须如何正确工作 https://go.dev/play/p/WrxE0vH6JSG

func init() {

    rand.Seed(1500929006430687579)

}


func goroutine(n int, wg *sync.WaitGroup, ch chan error) {

    defer wg.Done()

    defer fmt.Println("defer done")


    fmt.Println("num ", n)

    if n == 1 {

        ch <- fmt.Errorf("error")

    } else {

        ch <- nil

    }

}


func main() {

    var wg sync.WaitGroup

    var err error

    errs := make(chan error)

    platforms := 2

    types := 3

    for j := 0; j < platforms; j++ {

        wg.Add(1)

        for k := 0; k < types; k++ {

            wg.Add(1)

            n := rand.Intn(2)

            go goroutine(n, &wg, errs)

        }


        for k := 0; k < types; k++ {

            wg.Add(1)

            n := rand.Intn(2)

            go goroutine(n, &wg, errs)

        }

    }

    wg.Wait()

    err = <-errs

    fmt.Println(err)

}

我应该如何正确收集错误数组并完成所有等待组?


慕的地8271018
浏览 114回答 1
1回答

撒科打诨

在 Golang 中,通道类似于 bash ( |) 中的管道。但与用于将一个命令的输出传输到另一个命令的输入的 bash 管道不同,Go 通道用于在 goroutine 之间传输一些数据。您可以在此处阅读有关频道的更多信息。渠道有容量。当您没有为通道指定容量时,go 假定它的容量为 0。容量为零的信道通常称为unbuffered信道,而容量非零的信道称为buffered。当通道已满(通道中的元素数等于通道的容量)时,通道上的所有写操作 ( ->errs) 都会阻塞执行流程,直到<-errs出现读操作 ( ) 。在您的特定示例中,您有无缓冲通道(容量为 0 的通道)。因此,您通道上的任何写入操作 ( ->errs) 都将阻止执行,直到提供某些读取操作为止,因此您启动的所有 goroutine 都将被阻止,尽管只有一个 goroutine 能够在函数流移动时继续进行写入main操作转发读取操作 ( err = <-errs)。要解决您的问题,您可以创建一个额外的 goroutine,该 goroutine 会同时从通道读取数据,同时 goroutines 会写入通道。它看起来像这样:func init() {&nbsp; &nbsp; rand.Seed(1500929006430687579)}func goroutine(n int, wg *sync.WaitGroup, ch chan error) {&nbsp; &nbsp; defer fmt.Println("defer done")&nbsp; &nbsp; defer wg.Done()&nbsp; &nbsp; fmt.Println("num ", n)&nbsp; &nbsp; if n == 1 {&nbsp; &nbsp; &nbsp; &nbsp; ch <- fmt.Errorf("error")&nbsp; &nbsp; }}func main() {&nbsp; &nbsp; var wg sync.WaitGroup&nbsp; &nbsp; errs := make(chan error)&nbsp; &nbsp; platforms := 2&nbsp; &nbsp; types := 3&nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; for e := range errs {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Println(e)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }()&nbsp; &nbsp; for j := 0; j < platforms; j++ {&nbsp; &nbsp; &nbsp; &nbsp; for k := 0; k < types; k++ {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; wg.Add(1)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; n := rand.Intn(2)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; go goroutine(n, &wg, errs)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; for k := 0; k < types; k++ {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; wg.Add(1)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; n := rand.Intn(2)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; go goroutine(n, &wg, errs)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; wg.Wait()}此外,我在您的代码中重构了一些错误和不准确之处:你不应该在有错误的频道中写 nil 。如果你希望errschan 只包含错误,那么只有当你的函数执行时出现非零错误时才写在那里。您有一个额外的 wd.Add(1) 作为循环的开始,因此函数和函数j之间存在不平衡。3.AddDone此外,您添加defer fmt.Println("defer done")after&nbsp;defer wg.Done()but&nbsp;defers 构造的执行顺序与指定的顺序相反,因此放在defer fmt.Println("defer done")before 之前会更正确defer wg.Done(),这样“延迟完成”将真正表明所有先前的defers 已被执行。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go