Input Go-如何知道输出通道何时完成

Input Go-如何知道输出通道何时完成,input,concurrency,go,output,channel,Input,Concurrency,Go,Output,Channel,我试着按照Rob Pike在“并发性不是并行性”演讲中的例子,做了如下工作: 我启动了许多go例程,作为从输入通道读取数据、执行一些处理,然后通过输出通道发送结果的工作人员 然后,我启动另一个go例程,从某个源读取数据,并通过输入通道将其发送给工作人员。 最后,我想迭代输出通道中的所有结果,并对它们进行处理。 问题是,由于工作是在工作人员之间分配的,我不知道所有工作人员何时完成,因此我可以停止向输出通道询问更多结果,并且我的程序可以正常结束 当工作人员已完成将结果发送到输出通道时,了解的最佳实践

我试着按照Rob Pike在“并发性不是并行性”演讲中的例子,做了如下工作: 我启动了许多go例程,作为从输入通道读取数据、执行一些处理,然后通过输出通道发送结果的工作人员

然后,我启动另一个go例程,从某个源读取数据,并通过输入通道将其发送给工作人员。 最后,我想迭代输出通道中的所有结果,并对它们进行处理。 问题是,由于工作是在工作人员之间分配的,我不知道所有工作人员何时完成,因此我可以停止向输出通道询问更多结果,并且我的程序可以正常结束


当工作人员已完成将结果发送到输出通道时,了解的最佳实践是什么

我个人喜欢使用一个
sync.WaitGroup
。waitgroup是一个同步计数器,它有三种方法-
Wait()
Done()
Add()
。您要做的是增加waitgroup计数器的值,将其传递给工作人员,并让他们在完成后调用
Done()
。然后,您只需阻塞另一端的waitgroup,并在完成所有操作后关闭输出通道,从而导致输出处理器退出

基本上:

// create the wait group
wg := sync.WaitGroup{}

// this is the output channel
outchan := make(chan whatever)

// start the workers
for i := 0; i < N; i++ {
   wg.Add(1) //we increment by one the waitgroup's count

   //the worker pushes data onto the output channel and calls wg.Done() when done
   go work(&wg, outchan)
}

// this is our "waiter" - it blocks until all workers are done and closes the channel
go func() {
  wg.Wait()
  close(outchan)
}()

//this loop will exit automatically when outchan is closed
for item := range outchan {
   workWithIt(item)
}

// TADA!
//创建等待组
wg:=sync.WaitGroup{}
//这是输出通道
outchan:=制造(chan whatever)
//启动工人
对于i:=0;i
请允许我首先澄清您的术语:频道两端的误解可能会导致以后的问题。您询问“输出通道”和“输入通道”。没有这样的事情;只有频道

每个通道都有两端:输出(写入)端和输入(读取)端。我想这就是你的意思

现在回答你的问题

以最简单的情况为例:只有一个sender goroutine写入通道,另一端只有一个worker goroutine读取,通道没有缓冲。发送方goroutine将在写入每个项目时阻止它,直到该项目被消费。通常,这种情况在第一次发生时会很快发生。一旦第一项传递给工作者,工作者将很忙,发送者必须等待第二项才能传递。因此,乒乓球效应随之而来:作者或读者都很忙,但不是两者都忙。按照Rob Pike的描述,goroutines将是并发的,但并不总是并行执行

如果您有多个worker goroutine从通道中读取(其输入端由所有worker共享),发送者可以首先将一个项目分发给每个worker,但是在它们工作时它必须等待(类似于上面描述的乒乓球案例)。最后,当发送方发送了所有项目后,它就完成了工作。然而,读者可能还没有完成他们的工作。有时我们关心发件人提前完成,有时我们不关心。知道这种情况何时发生最容易通过等待小组完成(参见非高尔夫球手的答案和答案)

还有一个稍微复杂一点的替代方案:您可以使用返回通道来发送完成信号,而不是
WaitGroup
。这并不难做到,但在这种情况下,
WaitGroup
更简单,是首选

如果取而代之的是通道包含缓冲区,那么发送方发送其最后一个项目的时间点会更快。在限制情况下,当通道每个工作者有一个缓冲区空间时;这将允许发送者很快完成,然后,潜在地,继续做其他事情。(任何比这更多的缓冲都是浪费)

发送方的这种解耦允许一种完全异步的行为模式,这是使用其他技术堆栈(Node JS和JVM spring)的人所喜爱的。与他们不同的是,Go不需要你这么做,但你可以选择

早在90年代初,作为批量同步并行(BSP)策略工作的副作用,Leslie Valiant证明了有时非常简单的同步策略可能很便宜。关键因素是需要足够的并行松弛度(也称为过度并行度),以保持处理器内核繁忙。这意味着必须有足够多的其他工作要做,这样,如果某个特定的goroutine被阻塞一段时间,就真的不重要了

奇怪的是,这可能意味着使用较小数量的goroutine可能比使用较大数量的goroutine需要更多的注意

理解过度并行的影响是很有用的:如果整个网络都有过度并行,那么通常不需要花费额外的精力使所有事情都异步,因为CPU内核都会很忙

因此,尽管知道如何等待发送者完成是有用的,但大型应用程序可能不需要您以同样的方式关注

作为最后一个脚注,
WaitGroup
是BSP中使用的障碍。通过组合屏障和通道,您可以同时使用BSP和CSP。

var Z=“Z”
func循环(){
sc:=制造(成龙*字符串)
ss:=make([]字符串,0)
完成:=make(chan结构{},1)
go func(){
//1查询
切片1:=[]字符串{“a”、“b”、“c”}
//2工作组初始化
var wg1 sync.WaitGroup
wg1.添加(len(切片1))
//3循环->
环