从多个通道读取错误
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
循环,以便它从 c1
和 c2
读取,但我看不到这样做的方法non-sequentially?
您正在从关闭的频道中读取内容,它们总是 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.WaitGroup
或 errgroup
pkg 中的 errgroup.Group
。
像许多 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
循环,以便它从 c1
和 c2
读取,但我看不到这样做的方法non-sequentially?
您正在从关闭的频道中读取内容,它们总是 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.WaitGroup
或 errgroup
pkg 中的 errgroup.Group
。