Recursion 如何阻止(并加入)由未知数量的goroutine提供的通道?
我有一个递归函数。函数将根据所获得的数据使用各种不同的值来调用自己,因此递归的算术性和深度是未知的:每个调用可能会调用自己零次或多次。该函数可以返回任意数量的值 我想通过让goroutines和channels参与进来,将其并行化。Recursion 如何阻止(并加入)由未知数量的goroutine提供的通道?,recursion,go,channel,goroutine,Recursion,Go,Channel,Goroutine,我有一个递归函数。函数将根据所获得的数据使用各种不同的值来调用自己,因此递归的算术性和深度是未知的:每个调用可能会调用自己零次或多次。该函数可以返回任意数量的值 我想通过让goroutines和channels参与进来,将其并行化。内部的每个递归在其自己的goroutine中运行,并在通道上发回一个值。外部函数处理这些值 func outer(response []int) { results := make([]int) resultsChannel := make(chan int
内部
的每个递归在其自己的goroutine中运行,并在通道上发回一个值。外部函数处理这些值
func outer(response []int) {
results := make([]int)
resultsChannel := make(chan int)
inner := func(...) {
resultsChannel <- «some result»;
// Recurse in a new goroutine.
for _, recursionArgument in «some calculated data» {
go inner(recursionArgument)
}
}
go inner(«initial values»);
for {
result := <- resultsChannel
results = append(results, result)
// HELP! How do I decide when to break?
}
return results
}
func外部(响应[]内部){
结果:=make([]int)
结果通道:=make(chan int)
内部:=func(…){
结果通道您可以使用sync.WaitGroup
来管理生成的goroutine集合:在生成每个新goroutine之前调用Add(1)
,在每个goroutine完成时调用Done
。因此类似这样的操作:
var wg sync.WaitGroup
inner := func(...) {
...
// Recurse in a new goroutine.
for _, recursionArgument := range «some calculated data» {
wg.Add(1)
go inner(recursionArgument)
}
...
wg.Done()
}
wg.Add(1)
go inner(«initial values»)
现在等待wg
将告诉您所有goroutine何时完成
如果您正在读取某个频道的结果,那么当没有更多结果时,最明显的方法就是关闭该频道。您可以通过另一个goroutine来为我们完成此操作:
go func() {
wg.Wait()
close(resultsChannel)
}()
你现在应该可以通过range
浏览resultsChannel
来阅读所有的结果。你是否可以寻找一个sync.WaitGroup
?它的存在是最近向我指出的。很有可能。这看起来很有希望。@OldCurmudgeon医生说“请注意,具有正增量的呼叫必须在呼叫等待之前发生"。这意味着我肯定不能使用它?@Joe:这意味着内部
在生成子goroutine之前应该调用添加
。如果外部
希望内部
调用完成
,那么你知道在调用之前该组仍然处于活动状态。对不起@JamesHenstridge我不太清楚你在说什么重复。第一次运行internal
将运行,生成一个goroutine,继续,然后阻塞for
循环,或者Wait
。随后的递归将是异步的,因此如果我调用Add
insideinternal
,它将被调用任意次数,包括调用Wait
之后ed.另外,除非我遗漏了什么,否则我的整个问题是我无法调用Done
,因为我不知道我的递归何时结束..谢谢。所以对wg.Wait()
的调用发生在一些wg.Add(1)
执行之前,对吗?文档说可以吗“请注意,具有正增量的调用必须在调用等待之前发生”,但我不知道这是否意味着“至少有一个要添加的调用”或“所有调用”。您能否谈谈为什么选择不使用defer wg.Done()
?这不是建议的还是您的首选项?它似乎很适合这里,因为它不会过早退出。因为我们总是在调用内部之前调用Add(1)
,我们知道等待组的计数器在函数体中总是为正值(至少在完成调用之前)。由于我们知道此处无法触发等待
调用,因此可以安全地进一步增加计数器。如果让计数器提前瞬间达到零,则会出现争用条件(例如,将添加(1)
调用移到内部
)@OldCurmudgeon:在这里使用defer
可能会更干净一些。但是对于没有提前返回的简单情况,这并没有什么区别。对于示例代码,按顺序显示调用似乎更简单。还有一个问题。我想轮询频道,并(例如)将结果添加到结果片段中(举例来说,我希望在存储结果之前对其进行过滤)。也可以说我的通道大小已被限制。如果我等到递归停止,那么我唯一的解决方案就是使用另一个goroutine来监视结果通道
?