从嵌套在循环中的goroutine收集错误[duplicate]

aydmsdu9  于 2022-12-07  发布在  Go
关注(0)|答案(1)|浏览(159)

此问题在此处已有答案

Close multiple goroutine if an error occurs in one in go(3个答案)
Handle goroutine termination and error handling via error group?(1个答案)
25天前关闭。
我试图在循环中收集goroutine的错误,但不明白它必须如何正确工作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)
}

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

mzaanser

mzaanser1#

Golang渠道与bash管道类似但是与用于将一个命令的输出传输到另一个命令的输入的bash管道相比,Go通道用于在goroutine之间传输一些数据,你可以阅读更多关于通道here的信息,通道有容量,当你没有指定通道的容量时,Go会假设它的容量为0。具有零容量的通道通常称为unbuffered通道,而具有非零容量的通道称为buffered。当通道已满(通道中的元件数目等于通道的容量)时,通道(->errs)上的所有写入操作阻塞执行流,直到将呈现读取操作(<-errs)。
在您的特定示例中,有一个无缓冲通道(通道容量为0)。因此,任何写操作(->errs)将阻塞执行,直到提供一些读操作,因此,当main函数的流程前进到读操作时,你启动的所有goroutine都会被阻塞,尽管只有一个goroutine可以继续写操作(err = <-errs)的数据。
为了解决这个问题,你可以创建一个额外的goroutine,它同时从通道读取数据和向通道写入数据,如下所示:

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。如果你想让errs chan只包含错误,那么只有当你的函数执行时有非nil错误时才写。
1.你有一个额外的wd.Add(1)作为j循环的开始,所以Add函数和Done函数之间存在不平衡。
1.此外,您在defer wg.Done()之后添加defer fmt.Println("defer done"),但是defer的构造以与指定的相反的顺序执行,因此将defer fmt.Println("defer done")放在defer wg.Done()之前会更正确,这样“defer done”实际上将表示所有先前的defer都已执行。

相关问题