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

惯用的 goroutine 并发和错误处理

惯用的 goroutine 并发和错误处理

Go
慕工程0101907 2022-12-19 21:38:26
在下面的代码块中,我试图运行几个例程并为所有例程获取结果(无论是成功还是错误)。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但没能在这里找到对我有帮助的东西。欢迎提出任何建议。
查看完整描述

1 回答

?
SMILET

TA贡献1796条经验 获得超4个赞

Waitgroup 在此代码中是多余的。执行与等待通道结果的循环完美同步。在所有功能完成工作并从通道读取发布的结果之前,代码不会向前移动。仅当您的函数需要在结果发布到通道后执行任何工作时才需要 Waitgroup。


我也更喜欢稍微不同的实现。在发布的实现中,我们不会在每次执行函数时将结果和错误都发送到通道中。相反,我们可以只发送成功执行的结果,并在代码失败时只发送错误。


优点是简化了结果/错误处理。我们在没有nils.


在这个例子中,函数返回一个数字,我们发送它的默认值0以防出错。如果零可能是合法的函数执行结果,则从成功中过滤出不成功的执行结果可能会很复杂。


与错误相同。要检查我们是否有任何错误,我们可以使用像if len(errs) != 0.


package main


import (

    "fmt"

)


func processBatch(num int, errChan chan<- error, resultChan chan<- int) {

    if num == 3 {

        // no need to send result when it is meanenless

        // resultChan <- 0

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

    } else {

        square := num * num


        resultChan <- square


        // no need to send errror when it is nil

        // errChan <- nil

    }


}


func main() {

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


    resultChan := make(chan int)

    errChan := make(chan error)


    for i := range batches {

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

    }


    // use slices instead of arrays because legth varry now

    var results []int

    var errs []error


    // every time function executes it sends singe piece of data to one of two channels

    for range batches {

        select {

        case res := <-resultChan:

            results = append(results, res)

        case err := <-errChan:

            errs = append(errs, err)

        }

    }


    close(resultChan)

    close(errChan)


    fmt.Println(results)

    fmt.Println(errs)

}

https://go.dev/play/p/SYmfl8iGxgD


[25 1 16 4]

[goroutine 3's error returned]

如果你可以使用外部包,我们可以从一些multierr包中获益。例如,github.com/hashicorp/go-multierror。


查看完整回答
反对 回复 2022-12-19
  • 1 回答
  • 0 关注
  • 161 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

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

帮助反馈 APP下载

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

公众号

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