惯用的 goroutine 并发和错误处理

在下面的代码块中,我试图运行几个例程并为所有例程获取结果(无论是成功还是错误)。


package main

    

    import (

        "fmt"

        "sync"

    )

    

    func processBatch(num int, errChan chan<- error, resultChan chan<- int, wg *sync.WaitGroup) {

        defer wg.Done()

    

        if num == 3 {

            resultChan <- 0

            errChan <- fmt.Errorf("goroutine %d's error returned", num)

        } else {

            square := num * num

    

            resultChan <- square

    

            errChan <- nil

        }

    

    }

    

    func main() {

        var wg sync.WaitGroup

    

        batches := [5]int{1, 2, 3, 4, 5}

    

        resultChan := make(chan int)

        errChan := make(chan error)

    

        for i := range batches {

            wg.Add(1)

            go processBatch(batches[i], errChan, resultChan, &wg)

        }

    

        var results [5]int

        var err [5]error

        for i := range batches {

            results[i] = <-resultChan

            err[i] = <-errChan

        }

        wg.Wait()

        close(resultChan)

        close(errChan)

        fmt.Println(results)

        fmt.Println(err)

    }

游乐场:https ://go.dev/play/p/zA-Py9gDjce 此代码有效,我得到了我想要的结果,即:


[25 1 4 0 16]

[<nil> <nil> <nil> goroutine 3's error returned <nil>]

我想知道是否有更惯用的方法来实现这一目标。我浏览了 errgroup 包:https ://pkg.go.dev/golang.org/x/sync/errgroup但没能在这里找到对我有帮助的东西。欢迎提出任何建议。


慕工程0101907
浏览 163回答 1
1回答

SMILET

Waitgroup 在此代码中是多余的。执行与等待通道结果的循环完美同步。在所有功能完成工作并从通道读取发布的结果之前,代码不会向前移动。仅当您的函数需要在结果发布到通道后执行任何工作时才需要 Waitgroup。我也更喜欢稍微不同的实现。在发布的实现中,我们不会在每次执行函数时将结果和错误都发送到通道中。相反,我们可以只发送成功执行的结果,并在代码失败时只发送错误。优点是简化了结果/错误处理。我们在没有nils.在这个例子中,函数返回一个数字,我们发送它的默认值0以防出错。如果零可能是合法的函数执行结果,则从成功中过滤出不成功的执行结果可能会很复杂。与错误相同。要检查我们是否有任何错误,我们可以使用像if len(errs) != 0.package mainimport (&nbsp; &nbsp; "fmt")func processBatch(num int, errChan chan<- error, resultChan chan<- int) {&nbsp; &nbsp; if num == 3 {&nbsp; &nbsp; &nbsp; &nbsp; // no need to send result when it is meanenless&nbsp; &nbsp; &nbsp; &nbsp; // resultChan <- 0&nbsp; &nbsp; &nbsp; &nbsp; errChan <- fmt.Errorf("goroutine %d's error returned", num)&nbsp; &nbsp; } else {&nbsp; &nbsp; &nbsp; &nbsp; square := num * num&nbsp; &nbsp; &nbsp; &nbsp; resultChan <- square&nbsp; &nbsp; &nbsp; &nbsp; // no need to send errror when it is nil&nbsp; &nbsp; &nbsp; &nbsp; // errChan <- nil&nbsp; &nbsp; }}func main() {&nbsp; &nbsp; batches := [5]int{1, 2, 3, 4, 5}&nbsp; &nbsp; resultChan := make(chan int)&nbsp; &nbsp; errChan := make(chan error)&nbsp; &nbsp; for i := range batches {&nbsp; &nbsp; &nbsp; &nbsp; go processBatch(batches[i], errChan, resultChan)&nbsp; &nbsp; }&nbsp; &nbsp; // use slices instead of arrays because legth varry now&nbsp; &nbsp; var results []int&nbsp; &nbsp; var errs []error&nbsp; &nbsp; // every time function executes it sends singe piece of data to one of two channels&nbsp; &nbsp; for range batches {&nbsp; &nbsp; &nbsp; &nbsp; select {&nbsp; &nbsp; &nbsp; &nbsp; case res := <-resultChan:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; results = append(results, res)&nbsp; &nbsp; &nbsp; &nbsp; case err := <-errChan:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; errs = append(errs, err)&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; close(resultChan)&nbsp; &nbsp; close(errChan)&nbsp; &nbsp; fmt.Println(results)&nbsp; &nbsp; fmt.Println(errs)}https://go.dev/play/p/SYmfl8iGxgD[25 1 16 4][goroutine 3's error returned]如果你可以使用外部包,我们可以从一些multierr包中获益。例如,github.com/hashicorp/go-multierror。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go