Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/go/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
将数据从一个goroutine发送到多个其他goroutine_Go_Concurrency_Channel_Goroutine - Fatal编程技术网

将数据从一个goroutine发送到多个其他goroutine

将数据从一个goroutine发送到多个其他goroutine,go,concurrency,channel,goroutine,Go,Concurrency,Channel,Goroutine,在项目中,程序通过websocket接收数据。这些数据需要通过n个算法进行处理。算法的数量可以动态变化 我的尝试是创建一些发布/订阅模式,在其中可以动态启动和取消订阅。事实证明,这比预期的更具挑战性 以下是我的想法(基于): package-pubsub 进口( “上下文” “同步” “时间” ) 类型子结构{ sync.RWMutex subs[]*subsection 闭式布尔 } func New()*Pubsub{ ps:=&Pubsub{} ps.subs=[]*subsection{

在项目中,程序通过websocket接收数据。这些数据需要通过n个算法进行处理。算法的数量可以动态变化

我的尝试是创建一些发布/订阅模式,在其中可以动态启动和取消订阅。事实证明,这比预期的更具挑战性

以下是我的想法(基于):

package-pubsub
进口(
“上下文”
“同步”
“时间”
)
类型子结构{
sync.RWMutex
subs[]*subsection
闭式布尔
}
func New()*Pubsub{
ps:=&Pubsub{}
ps.subs=[]*subsection{}
返回ps
}
func(ps*Pubsub)发布(消息接口{}){
ps.RLock()
延迟ps.RUnlock()
如果ps关闭{
返回
}
对于u,sub:=范围ps.subs{
//问题1:这些goroutine显然不能正确退出。。。
go func(ch chan接口{}){

ch在深入探讨您的解决方案及其问题之前,让我再次推荐本答案中介绍的另一种经纪人方法:

现在来看看你的解决方案


无论何时启动goroutine,都要考虑它将如何结束,并确保如果goroutine在应用程序的生命周期中不应该运行,它会结束

// ISSUE1: These goroutines apparently do not exit properly... 
go func(ch chan interface{}) {
    ch <- msg
}(sub.Data)
如果发送
ch
可以继续,则会发生。如果不能继续,则会立即选择
默认
分支。您必须决定接下来要做什么。是否可以“丢失”消息?是否可以等待一段时间直到“放弃”?或者是否可以启动goroutine来执行此操作(但是你会回到我们试图解决的问题上)?或者在客户端能够接收到来自频道的消息之前,被阻止是可以接受的

选择一个合理的高缓冲区,如果你遇到一个情况,当它仍然变满时,可以阻止,直到客户端可以前进并接收消息。如果不能,那么你的整个应用程序可能处于不可接受的状态,并且可以接受“挂起”或“崩溃”

关闭通道是向接收方发出的信号,表示不再在通道上发送任何值。因此,关闭通道始终是发送方的工作(和责任)。启动goroutine关闭通道时,您将该工作和责任“交”给另一个“实体”(goroutine)这将不会同步到发送方。这可能很容易导致恐慌(在封闭通道上发送是运行时恐慌,其他公理请参见)。不要这样做

是的,这是必要的,因为您启动了goroutines以发送。如果您不这样做,则可以关闭“就地”,而不启动goroutine,因为这样发送方和closer将是同一个实体:
Pubsub
本身,其发送和关闭操作由互斥锁保护。因此,解决第一个问题自然解决了第二个问题


通常,如果一个通道有多个发送方,则必须协调关闭通道。必须有一个实体(通常不是任何发送方)这将等待所有发件人完成,实际上使用了一个
sync.WaitGroup
,然后单个实体可以安全地关闭通道。请参阅。

请参阅可能的重复:@icza:您的回复中发布的代理似乎很吸引人,因为它看起来非常简单-我正在尝试一下。不过,我仍然对解决此问题的提示感兴趣他在我的方法中提出了问题;这将帮助我学习……谢谢!我采用了你提到的实现。拥有一个执行操作的主循环是有意义的,并且消除了对
互斥体的需要。你的实现基本上有三个变化:
订阅
结构,主要是提供挂起它的
Unsubscribe()
func。另外
Subscribe()
提供了一个上下文,允许使用者在Pubsub/Broker停止时做出反应。最后
start()
与以前的实现相比,不是pulic,而是通过构造函数触发的,以保持API稳定:参见@sontags是的,它现在看起来简单多了。我仍然相信使用映射存储订阅者是值得的(但这取决于有多少订阅者,以及他们订阅/取消订阅的频率)。我还认为,如果这是您所描述的所有希望的功能,那么就不需要
subsection
类型。但是如果将来需要添加,并且API不会更改,那么它会更灵活。
// ISSUE1: These goroutines apparently do not exit properly... 
go func(ch chan interface{}) {
    ch <- msg
}(sub.Data)
select {
case ch <- msg:
default:
    // ch's buffer is full, we cannot deliver now
}
// ISSUE2: close the channel async with a delay to ensure
// nothing will be written to the channel anymore
// via a pending goroutine from Publish()
go func(ch chan interface{}) {
    time.Sleep(500 * time.Millisecond)
    close(ch)
}(s.Data)