Go语言 一个例程遇到错误就报告错误

ovfsdjhp  于 2024-01-04  发布在  Go
关注(0)|答案(1)|浏览(97)

我试图找出一个例程遇到错误时立即报告错误的基本模式。

func doSomeWork(num int, errChan chan <- error) {
        if num >= 10 {
                errChan <- errors.New("Greater than 10")
        }
        return
}

const numWorkers int = 25

func run() error {
        var wg sync.WaitGroup
        errChan := make(chan error)
        for i := 0; i < numWorkers; i++ {
                wg.Add(1)
                go func(num int) {
                        defer wg.Done()
                        doSomeWork(num, errChan)
                }(i)
        }
        go func() {
                wg.Wait()
                close(errChan)
        }()
        err, ok := <-errChan
        if !ok {
                return nil
        }
        return  err
}

字符串
这和预期的一样,当numWorkers = 10时,它返回nil< 10 and error when numworker is >。我实际上并不关心工人的数量,这只是一个简单的例子。但是,它感觉不是很习惯,当一个例程失败时,可能会有一些例程泄漏,因为其他例程仍然在运行,等待所有例程的例程也仍然在运行。有没有更习惯的方法来做到这一点,一旦一个例程失败,它也会停止所有例程?

zlhcx6iw

zlhcx6iw1#

代码是封闭的,但是当发送多个错误时会泄漏goroutine。第一个发送errChan的worker会退出,因为主goroutine会接收到这个值。所有发送到errChan的worker会永远阻塞,因为没有goroutine接收这个值。wg.Wait(); close(errChan) goroutine也会永远阻塞,等待worker完成。
修复是双重的:使用非阻塞发送到通道,并确保errChan可以接收至少一个值。
请参阅下面的评论以了解更多详细信息:

func run() error {
    var wg sync.WaitGroup
    // Create channel with capacity one to ensure that
    // a goroutine can send to the channel before this
    // goroutine is ready to receive.
    errChan := make(chan error, 1)  // <-- note 1 here

    for i := 0; i < numWorkers; i++ {
        wg.Add(1)
        go func(num int) {
            defer wg.Done()
            doSomeWork(num, errChan)
        }(i)
    }
    go func() {
        wg.Wait()
        // All goroutines are done, but the main goroutine
        // will be waiting if there were no errors.  Close 
        // the channel so that the main goroutine receives
        // a zero error value.
        close(errChan)
    }()
    // The if statement in the original is not needed because
    // the zero value of the error interface is nil. 
    return <-errChan
}

func doSomeWork(num int, errChan chan<- error) {
    if num >= 10 {
        select {
        case errChan <- errors.New("Greater than 10"):
             // Successful send on channel
        default:
             // Channel is not ready. This implies that
             // at least one other goroutine sent an error
             // to the channel.  There's nothing for us to
             // do here. error.
        }
    }
}

字符串
Run the program on the Playground

相关问题