Go 谁应该分配通道,即被叫方的呼叫方?

Go 谁应该分配通道,即被叫方的呼叫方?,go,design-patterns,concurrency,observers,channels,Go,Design Patterns,Concurrency,Observers,Channels,我正在尝试用Go设计一个简单的家庭自动化程序。我的灯泡在状态改变时会返回消息流。这可以通过回调来处理,或者使用Go中的通道可能更好 通过回调,很明显灯泡API的调用者实现了一个特定的函数签名,并将函数传递给库。当灯泡改变状态或发生通信错误时,将调用该函数。当客户端不再对更新感兴趣时,库将分配要调用的done函数。比如说 type DeviceUpdate = func(Device, error) type Done = func() func (client *Client) Obse

我正在尝试用Go设计一个简单的家庭自动化程序。我的灯泡在状态改变时会返回消息流。这可以通过回调来处理,或者使用Go中的通道可能更好

通过回调,很明显灯泡API的调用者实现了一个特定的函数签名,并将函数传递给库。当灯泡改变状态或发生通信错误时,将调用该函数。当客户端不再对更新感兴趣时,库将分配要调用的done函数。比如说

 type DeviceUpdate = func(Device, error)
 type Done = func()

 func (client *Client) ObserveDevice(deviceID int, deviceUpdate DeviceUpdate) (Done, error) {
在我的例子中,通道似乎提供了一个更好的抽象,因为它们可以很容易地合并,可以添加缓冲,可以很容易地编织中间件等等。我对上述声明的通道表示有点纠结。在这种情况下,Go的正确惯例是什么,即数据通道、错误通道和整个系统上的完成/关闭操作

我认为通道应该由调用方分配,类似于函数。这样,调用者可以在激活整个设置之前正确地实例化通道并连接中间件

func (client *Client) ObserveDevice(deviceID int, data chan Device, done chan struct{}, err chan error) error {
另一方面,对于用户代码来说,使用灯泡库分配通道似乎要简单得多——您只需调用一个函数,无需分配3个通道。同时,Go time模块似乎勇敢地走这条路

在我的情况下,让库分配通道将导致如下签名

func (client *Client) ObserveDevice(deviceID int) (data chan Device, done chan struct{}, err chan error) {
那么,什么是渠道分配的Go标准实践呢?是呼叫方对被叫方还是生产者对消费者

当从网络流中导入更新时,通常的做法是有3个通道-数据、错误和完成


我在关于频道和Go并发性的文章和视频中做了一些挖掘,但找不到(可能是因为懒惰)有记录的最佳实践。我假设示例中暗示了调用者分配通道。看,

我从来没有听说过谁创建频道的最佳实践。这其实并不重要,什么是最好的取决于情况。另一方面,应该关闭通道的始终是发送方。即使“发送方应该始终关闭通道”也有一些例外。这只是一个很好的简单经验法则。
从网络流中导入更新时通常有3个通道-数据、错误和完成?
不太可能。一个通道足以产生一个同时包含数据和错误的类型。关闭它将表示没有更多的数据可读取。看这个例子。@mh cbon这很有道理。有一个单独的错误通道会使排序变得混乱。感谢您指出这一点。@mh cbon管道链接包括一个并行md5校验和示例,它利用两个通道:一个用于结果,另一个用于错误(实际上只有一个错误)。我通常更喜欢在单个通道上发送的“结果”结构中包含错误,因为错误可能发生在结果生成逻辑之外。