从多个渠道读取错误信息

huangapple go评论95阅读模式
英文:

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循环,使其从c1c2中读取,但我无法找到一种非顺序执行的方法。

你可以在这里查看代码: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 &lt;-chan interface{}, ns []int) &lt;-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(&quot;problem...\n&quot;)
				}
				select {
				case &lt;-done:
					return
				case results &lt;- 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 = &lt;-c1:
			if r.Error != nil {
				fmt.Printf(&quot;error1: %v&quot;, r.Error)
				return
			}
			fmt.Printf(&quot;Response1: %v\n&quot;, r.Response)
		case r = &lt;-c2:
			if r.Error != nil {
				fmt.Printf(&quot;error2: %v&quot;, r.Error)
				return
			}
			fmt.Printf(&quot;Response2: %v\n&quot;, 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?

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

答案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.WaitGrouperrgroup 包中的 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 := &lt;-c1:
if !ok {
c1 = nil
if c2 == nil {
return
}
continue
}
if r.Error != nil {
fmt.Printf(&quot;error1: %v&quot;, r.Error)
return
}
fmt.Printf(&quot;Response1: %v\n&quot;, 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.

huangapple
  • 本文由 发表于 2022年5月28日 22:16:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/72416383.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定