【问题标题】:How to handle asynchronous errors in Go?如何处理 Go 中的异步错误?
【发布时间】:2015-07-18 10:31:44
【问题描述】:

我正在开发我的第一个真正的 Go 项目,一个消息传递 API。我使用通道在用户 goroutine 和使用线程不安全、基于事件的 C 协议库的库 goroutine 之间传递消息和其他数据。详情https://github.com/apache/qpid-proton/blob/master/proton-c/bindings/go/README.md

我的问题有两个相关的部分:

1.处理跨渠道错误的常见习语是什么?

一端的goroutine炸了,我如何确保另一端解除阻塞,得到error值并且以后不会再次被阻塞?

给读者:

  • 我可以关闭频道,但没有错误信息。
  • 我可以通过struct { data, error }
  • 或使用第二个频道。

优点和缺点?其他想法?

对于作家:我不能不慌不忙地关闭,所以我想我需要第二个频道。这是惯用语吗?

select {
    case sendChan <- data: sentOk()
    case err := <- errChan: oops(err)
}

关闭后我也无法写入,因此我需要将错误存储在某处并在尝试写入之前检查。还有其他方法吗?

2。在 API 中公开通道。

我需要通道来传递错误信息:我应该将这些通道设为公共字段还是将它们隐藏在方法中?

有一个权衡,我没有经验来评估它:

  • 暴露通道允许用户直接选择,但它要求他们正确实施错误处理模式(在写入之前检查错误,选择错误以及写入)。这似乎很复杂且容易出错,但可能是因为我没有经验。

  • 在方法中隐藏通道可简化并强制正确使用库。但是现在异步用户必须创建自己的 goroutine 和通道。他们可能只是复制图书馆已经做的事情,这很愚蠢。路径上还有一个额外的 goroutine 和通道。也许这没什么大不了的,但数据通道是我的库的关键路径,我认为它必须与错误通道一起隐藏。

我可以做到这两点:为高级​​用户公开渠道为有简单需求的人提供一个简单的方法包装器。这更值得支持,但如果任何一个都不能满足所有情况,那么它是值得的。

标准的 net.Conn 使用阻塞方法,而不是通道,我编写了 goroutine 将数据泵送到我的 C 事件循环通道,所以我知道它可以做到,但我并不觉得它微不足道。 net.Conn 将系统调用而不是通道封装在下面,因此“暴露通道”不是一种选择。是否有任何标准库导出带有错误处理的通道? (time.After不算,没有错误)

非常感谢! 艾伦

【问题讨论】:

    标签: error-handling go channel


    【解决方案1】:

    您的问题有点笼统,但我会根据我编写高并发代码的经验尝试提供一些指导...

    就我个人而言,我认为将通道作为对象的属性,在一个很有帮助的NewMyObject() *MyObject 方法中进行初始化是很好的设计模式。它使得使用该对象的代码不必在每次想要调用该类型提供的一些异步方法时都进行样板设置。

    For readers: I can close the channel, but no error info. I could pass a struct { data, error } or use a second channel. Pros &amp; cons? Other ideas?

    通过关闭中止通道让阅读器发出返回信号。如果数据或错误通道已关闭,读者应该简单地使用temp, err := &lt;-FromChannel 范例并继续执行。这应该可以防止工作人员的“在关闭的通道上发送”恐慌错误,因为他们将关闭他们的通道并返回。当err != nil 读者会知道继续前进。

    For writers: I can't close without a panic so I guess I need a second channel. Is this idiomatic?

    是的。可悲的是,我对通道的单向行为非常生气,尽管它应该被抽象化。无论如何,它不是。在我的代码中,我不会在异步工作的对象上定义它。我更喜欢的范例是使用关闭信号(因为在通道上发送 a 不是一对多的,只有一个 goroutine 会读取它)。相反,我在调用代码中分配 abort 通道,如果需要关闭你关闭abort 通道,所有在该通道上进行异步工作的 goroutine 进行清理并返回。您还应该使用 WaitGroup,这样您就可以在继续之前等待 goroutines 返回。

    所以我的基本总结;

    1) 让异步方法的调用者发出信号停止,而不是相反。等待组更好地用于协调他们的回报

    2) 在调用代码中使用sync.WaitGroup 以了解您的 goroutine 何时完成,以便您继续前进

    3)在调用代码中分配你的错误通道,并利用关闭通道产生的一对多信号;如果您在调用者中分配的通道上发送,则只有一个实例会从中读取。如果您在每个实例上放置一个,则必须迭代一组实例以在每个实例上发送。

    4) 如果您的类型提供在后台运行的异步方法,请在其初始化程序中设置要读取的通道,记录异步方法,说明在哪里侦听数据,提供非同步方法的示例-阻塞选择,将中止通道传递到异步方法并侦听方法数据和错误通道。如果您需要终止一个例程,您可以通过关闭它拥有的一个通道来完成此操作,而不是通过关闭调用者abort 通道来杀死它们。

    希望这一切都有意义。

    【讨论】:

    • 这真的很有帮助。我喜欢调用者创建的中止通道。我已经为消息确认这样做了,但不知何故我并没有将其视为错误。
    • 我不遵循这个:“我永远不会让工作人员关闭它写入的通道”我认为只有从写入端关闭才是安全的。谁应该关闭频道?
    • @AlanConway 是的,这没有意义......我会编辑它。作者应该关闭它的频道并从中止案例中返回。
    • 有一些简单的代码示例作为答案的补充会非常有用。
    猜你喜欢
    • 2015-04-15
    • 2019-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-25
    • 1970-01-01
    • 2020-06-29
    • 1970-01-01
    相关资源
    最近更新 更多