从多个通道读取错误

Reading errors from multiple channels

像许多 go 程序员一样,到目前为止都避免对通道做任何重要的事情,所以这个 apparent简单的场景让我感到难过!

我想要多个 goroutines 发送由单个 parent 检查的结果。如果有任何发送错误,parent 应该发出信号让它们全部停止。 parent 应该读取结果 non-sequentially。

只要其中一个 goroutine 确实发送了错误,即如果您在 nums 内的 11 中发表评论,则此代码有效,否则我们将永远陷入 for 循环。

func main() {
    type Result struct {
        Error    error
        Response int
    }
    checkStatus := func(done <-chan interface{}, ns []int) <-chan Result {
        results := make(chan Result)
        go func() {
            defer close(results)
            for _, n := range ns {
                result := Result{Response: n}
                if n == 11 {
                    result.Error = fmt.Errorf("problem...\n")
                }
                select {
                case <-done:
                    return
                case results <- result:
                }
            }
        }()
        return results
    }

    done := make(chan interface{})
    defer close(done)

    nums := []int{1, 2, 3, 4, 5 /*11,*/, 6, 7, 8, 9, 10}
    c1 := checkStatus(done, nums[:5])
    c2 := checkStatus(done, nums[5:])
    for {
        var r Result
        select {
        case r = <-c1:
            if r.Error != nil {
                fmt.Printf("error1: %v", r.Error)
                return
            }
            fmt.Printf("Response1: %v\n", r.Response)
        case r = <-c2:
            if r.Error != nil {
                fmt.Printf("error2: %v", r.Error)
                return
            }
            fmt.Printf("Response2: %v\n", r.Response)
        }
    }
}

我能看到修复它的唯一方法是更改​​ for 循环,以便它从 c1c2 读取,但我看不到这样做的方法non-sequentially?

https://go.dev/play/p/7dRPMDn1Za2

您正在从关闭的频道中读取内容,它们总是 return 零值。您可以做的是在从频道读取时使用逗号 ok 习惯用法关闭 select 大小写,然后将频道分配给 nil。如果另一个也为零,则 return.

case 频道为 nil 时永远不会运行。

只需扩展您的代码(与 c2 的情况类似):

case r, ok := <-c1:
    if !ok {
        c1 = nil
        if c2 == nil {
            return
        }
        continue
    }
    if r.Error != nil {
        fmt.Printf("error1: %v", r.Error)
        return
    }
    fmt.Printf("Response1: %v\n", r.Response)

但我宁愿尝试重构整个实现。

您可以考虑使用 sync pkg 中的 sync.WaitGrouperrgroup pkg 中的 errgroup.Group