为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
慕的地8271018 2023-03-15 14:30:02
我正在尝试从循环中的 goroutines 收集错误,但不明白它必须如何正确工作 https://go.dev/play/p/WrxE0vH6JSGfunc 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)}我应该如何正确收集错误数组并完成所有等待组?
查看完整描述

1 回答

?
撒科打诨

TA贡献1934条经验 获得超2个赞

在 Golang 中,通道类似于 bash ( |) 中的管道。但与用于将一个命令的输出传输到另一个命令的输入的 bash 管道不同,Go 通道用于在 goroutine 之间传输一些数据。您可以在此处阅读有关频道的更多信息。渠道有容量。当您没有为通道指定容量时,go 假定它的容量为 0。容量为零的信道通常称为unbuffered信道,而容量非零的信道称为buffered。当通道已满(通道中的元素数等于通道的容量)时,通道上的所有写操作 ( ->errs) 都会阻塞执行流程,直到<-errs出现读操作 ( ) 。


在您的特定示例中,您有无缓冲通道(容量为 0 的通道)。因此,您通道上的任何写入操作 ( ->errs) 都将阻止执行,直到提供某些读取操作为止,因此您启动的所有 goroutine 都将被阻止,尽管只有一个 goroutine 能够在函数流移动时继续进行写入main操作转发读取操作 ( err = <-errs)。


要解决您的问题,您可以创建一个额外的 goroutine,该 goroutine 会同时从通道读取数据,同时 goroutines 会写入通道。它看起来像这样:


func init() {

    rand.Seed(1500929006430687579)

}


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

    defer fmt.Println("defer done")

    defer wg.Done()


    fmt.Println("num ", n)

    if n == 1 {

        ch <- fmt.Errorf("error")

    }

}


func main() {

    var wg sync.WaitGroup

    errs := make(chan error)

    platforms := 2

    types := 3


    go func() {

        for e := range errs {

            fmt.Println(e)

        }

    }()


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

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

}

此外,我在您的代码中重构了一些错误和不准确之处:

  1. 你不应该在有错误的频道中写 nil 。如果你希望errschan 只包含错误,那么只有当你的函数执行时出现非零错误时才写在那里。

  2. 您有一个额外的 wd.Add(1) 作为循环的开始,因此函数和函数j之间存在不平衡。3.AddDone

  3. 此外,您添加defer fmt.Println("defer done")after defer wg.Done()but defers 构造的执行顺序与指定的顺序相反,因此放在defer fmt.Println("defer done")before 之前会更正确defer wg.Done(),这样“延迟完成”将真正表明所有先前的defers 已被执行。


查看完整回答
反对 回复 2023-03-15
  • 1 回答
  • 0 关注
  • 113 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号