英文:
Reading errors from multiple channels
问题
像许多Go程序员一样,到目前为止,我一直避免在通道中执行任何重要操作,所以这个看似简单的场景让我困惑了!
我想要多个goroutine发送结果,并由一个单独的父goroutine进行检查。如果有任何一个goroutine发送了错误,父goroutine应该发出信号让它们全部停止。父goroutine应该非顺序地读取结果。
这段代码在其中一个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
中读取,但我无法找到一种非顺序执行的方法。
你可以在这里查看代码:https://go.dev/play/p/7dRPMDn1Za2
英文:
Like many go programmers, have so far avoided doing anything significant with channels so this apparently simple scenario has got me stumped!
I want multiple goroutines to send results checked by a single parent. If any send an error, the parent should signal for them all to stop. The parent should read the results non-sequentially.
This code works provided one of the goroutines does send an error i.e. if you comment in the 11
inside nums otherwise we just get stuck in the for loop forever.
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)
}
}
}
The only way I can see to fix it is the change the for
loop so it reads from c1
and c2
but I can't see a way to do this non-sequentially?
答案1
得分: 1
你正在读取关闭的通道,它们总是返回零值。你可以通过在从通道读取时使用逗号 ok 习语关闭 select case,并将通道赋值为 nil。如果另一个通道也是 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
包中的 sync.WaitGroup
或 errgroup
包中的 errgroup.Group
。
英文:
You're reading from closed channels and they always return a zero value. What you can do is to turn off a select case by using a comma ok idiom while reading from a channel and then assign the channel to nil. If the other one is nil as well then return.
case
with a nil channel never runs.
Just by extending your code (similarly for the case with 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)
But I'd rather try to refactor the whole implementation.
What you can consider is using sync.WaitGroup
from the sync
pkg or errgroup.Group
from the errgroup
pkg.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论