像许多go程序员一样,到目前为止,我一直避免使用通道做任何重要的事情,所以这个看似简单的场景让我难住了!
我想要多个例程发送由单个父程序检查的结果。如果其中任何一个发送错误,父进程应该发出信号让它们全部停止。父程序应该非顺序地读取结果。
如果其中一个程序发送错误,即如果您在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: %vn", r.Response)
case r = <-c2:
if r.Error != nil {
fmt.Printf("error2: %v", r.Error)
return
}
fmt.Printf("Response2: %vn", r.Response)
}
}
}
我能看到修复它的唯一方法是改变for
循环,所以它从c1
和c2
读取,但我看不到一种方法来做这个非顺序的?
https://go.dev/play/p/7dRPMDn1Za2
从关闭的通道读取,它们总是返回零值。您可以做的是在读取通道时使用逗号ok来关闭选择情况,然后将通道赋值为nil。如果另一个也是nil,则返回。
没有通道的case
永远不会运行。
只需扩展您的代码(类似于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: %vn", r.Response)
但是我宁愿尝试重构整个实现。
您可以考虑使用sync
pkg中的sync.WaitGroup
或errgroup
pkg中的errgroup.Group
。