我正在开发我的第一个真正的Go项目,一个消息传递API。我使用通道在用户goroutine和使用线程不安全,基于事件的C协议库的库goroutine之间传递消息和其他数据。有关详细信息this
我的问题在于2个相关部分:
1。跨渠道处理错误的常用习惯是什么?
一端的goroutine会爆炸,我如何确保另一端取消阻止,获得error
值并且之后不再被阻止?
读者:
struct { data, error }
优点&利弊?其他想法?
对于作家:我不能没有恐慌地关闭,所以我想我需要第二个频道。这是惯用的吗?
select {
case sendChan <- data: sentOk()
case err := <- errChan: oops(err)
}
我也不能在关闭后写,所以我需要将错误存储在某处并在尝试写入之前进行检查。还有其他方法吗?
2。在API中公开渠道。
我需要通道来传递错误信息:我应该将这些频道设为公共字段还是将其隐藏在方法中?
有一个权衡,我没有经验来评估它:
公开渠道让用户可以直接选择,但要求他们正确地处理错误处理模式(在写入前检查错误,选择错误以及写入)。这似乎很复杂且容易出错,但也许是因为我没有经验丰富。
隐藏方法中的通道可以简化并强制正确使用库。但是现在异步用户必须创建自己的goroutine和channel。他们可能只是复制了库已经存在的东西,这很愚蠢。此外,路径上还有一个额外的goroutine和频道。也许这不是什么大不了的事,但数据通道是我库的关键路径,我认为它必须与错误通道一起隐藏。
我可以做到这两点:为高级用户公开频道和为有简单需求的人提供简单的方法包装。如果不能单独适用于所有情况,那么更多的是支持但值得。
标准net.Conn使用阻塞方法,而不是通道,我写了goroutines来将数据泵送到我的C事件循环通道,所以我知道它可以完成,但我没有发现它是微不足道的。 net.Conn包装sytem调用而不是下面的通道所以&#34;暴露通道&#34;不是一种选择。任何标准库导出通道都有错误处理吗? (时间。没有计算,没有错误)
非常感谢! 艾伦
答案 0 :(得分:3)
你的问题有点广泛但我会尝试根据我编写高度并发代码的经验给出一些指导......
就我个人而言,我认为将通道设置为在一个很好的有用的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 & cons? Other ideas?
通过关闭中止通道让读者发出信号。读者应该只使用temp, err := <-FromChannel
范例,并在数据或错误通道关闭时继续执行。这应该可以防止工作人员发出“发送关闭频道”的恐慌错误,因为他们将关闭他们的频道并返回。当err != nil
读者知道继续前进时。
For writers: I can't close without a panic so I guess I need a second channel. Is this idiomatic?
是。可悲的是,我对频道的单向行为感到很生气,尽管它应该被抽象出来。无论如何,事实并非如此。在我的代码中, 不会 在异步工作的对象上定义它。我更喜欢的范例是使用关闭信号(因为在通道上发送不是一对多,只有一个goroutine会读取它)。相反,我在调用代码中分配中止通道,如果需要关闭,则关闭abort
通道,并且正在监听该通道的异步工作的所有goroutine进行清理并返回。您还应该使用WaitGroup,以便在继续之前等待goroutines返回。
所以我的基本摘要;
1)让异步方法的调用者发出停止信号的时间,而不是反过来。 waitgroup更适合用于协调其返回
2)在调用代码中使用sync.WaitGroup
来了解你的goroutine何时完成,以便继续前进
3)在调用代码中分配您的错误通道,并利用关闭通道产生的一对多信号;如果您发送在调用者中分配的通道,则只有一个实例将从中读取。如果在每个实例上放置一个实例,则必须迭代一组实例以发送每个实例。
4)如果你的类型提供在后台工作的异步方法,设置通道以读取其初始化程序,记录异步方法说明在哪里监听数据,提供一个非示例-blocking选择将中止通道传递给异步方法并侦听方法数据和错误通道。如果您需要杀死一个例程,您可以通过关闭其拥有的一个频道来完成此操作,而不是通过关闭呼叫者abort
频道将其全部删除。
希望一切都有道理。