英文:
How can I block (and join) on a channel fed by an unknown number of goroutines?
问题
我有一个递归函数。根据它接收到的数据,该函数将使用不同的值调用自身,因此递归的参数个数和深度是未知的:每次调用可能会调用自身零次或多次。该函数可以返回任意数量的值。
我想通过使用goroutine和channel来并行化它。inner
的每次递归都在自己的goroutine中运行,并在通道上发送一个值。外部函数处理这些值。
func outer(response []int) {
results := make([]int)
resultsChannel := make(chan int)
inner := func(...) {
resultsChannel <- «一些结果»;
// 在新的goroutine中进行递归。
for _, recursionArgument := range «一些计算得到的数据» {
go inner(recursionArgument)
}
}
go inner(«初始值»);
for {
result := <- resultsChannel
results = append(results, result)
// 帮助!我该如何决定何时退出循环?
}
return results
}
问题出在如何跳出结果通道循环。由于递归的“形状”(参数个数和深度未知),我无法说“在n个事件之后结束”,也无法发送一个特殊值。
如何检测所有递归都已完成并从outer
函数返回?有没有更好的方法来解决这个问题?
英文:
I have a recursive function. The function will call itself with various different values depending on the data it gets, so the arity and depth of recursion is not known: each call may call itself zero or more times. The function may return any number of values.
I want to parallelise it by getting goroutines and channels involved. Each recursion of inner
runs in its own goroutine, and sends back a value on the channel. The outer function deals with those values.
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
}
The problem comes with escaping the results channel loop. Because of the 'shape' of the recursion (unknown arity and depth) I can't say "finish after n events" and I can't send a sentinel value.
How do I detect when all my recursions have happened and return from outer
? Is there a better way to approach this?
答案1
得分: 3
你可以使用sync.WaitGroup
来管理你所创建的goroutine集合:在每个新的goroutine生成之前调用Add(1)
,在每个goroutine完成时调用Done
。代码示例如下:
var wg sync.WaitGroup
inner := func(...) {
...
// 在一个新的goroutine中递归调用。
for _, recursionArgument := range «一些计算得到的数据» {
wg.Add(1)
go inner(recursionArgument)
}
...
wg.Done()
}
wg.Add(1)
go inner(«初始值»)
现在,等待`wg`将告诉你所有的goroutine何时完成。
如果你从一个channel中读取结果,判断没有更多结果的明显方法是关闭该channel。你可以通过另一个goroutine来实现这一点:
go func() {
wg.Wait()
close(resultsChannel)
}()
现在,你可以简单地使用`range`遍历`resultsChannel`来读取所有的结果。
英文:
You can use a sync.WaitGroup
to manage the collection of goroutines you spawn: call Add(1)
before spawning each new goroutine, and Done
when each goroutine completes. So something like this:
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»)
Now waiting on wg
will tell you when all the goroutines have completed.
If you are reading the results from a channel, the obvious way to tell when there are no more results is by closing the channel. You can achieve this through another goroutine to do this for us:
go func() {
wg.Wait()
close(resultsChannel)
}()
You should now be able to simply range
over resultsChannel
to read all the results.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论