我正在使用 goroutines 实现一个(某种)组合回溯算法。我的问题可以表示为具有一定程度/分布的树,我想访问每片叶子并根据所采用的路径计算结果。在给定的级别上,我想生成 goroutines 来同时处理子问题,即如果我有一个度数为 3 的树并且我想在级别 2 之后开始并发,我会生成 3*3=9 goroutines 继续处理子问题并发。
func main() {
cRes := make(chan string, 100)
res := []string{}
numLevels := 5
spread := 3
startConcurrencyAtLevel := 2
nTree("", numLevels, spread, startConcurrencyAtLevel, cRes)
for {
select {
case r := <-cRes:
res = append(res, r)
case <-time.After(10 * time.Second):
ft.Println("Caculation timed out")
fmt.Println(len(res), math.Pow(float64(spread), float64(numLevels)))
return
}
}
}
func nTree(path string, maxLevels int, spread int, startConcurrencyAtLevel int, cRes chan string) {
if len(path) == maxLevels {
// some longer running task here associated with the found path, also using a lookup table
// real problem actually returns not the path but the result if it satisfies some condition
cRes <- path
return
}
for i := 1; i <= spread; i++ {
nextPath := path + fmt.Sprint(i)
if len(path) == startConcurrencyAtLevel {
go nTree(nextPath, maxLevels, spread, startConcurrencyAtLevel, cRes)
} else {
nTree(nextPath, maxLevels, spread, startConcurrencyAtLevel, cRes)
}
}
}
上面的代码有效,但是我依赖于 for select 语句超时。我正在寻找一种在所有 goroutine 完成后立即继续 main() 的方法,即所有子问题都已处理。
我已经想出了两种可能的(不受欢迎的/不雅的)解决方案:
使用互斥锁保护的结果映射 + 等待组而不是基于通道的方法应该可以解决问题,但我很好奇是否有一个简洁的通道解决方案。
使用退出通道(int 类型)。每次生成一个 goroutine 时,退出通道都会得到一个 +1 int,每次在叶子中完成计算时,它都会得到一个 -1 int 并且调用者对这些值求和。请参阅以下代码片段,但这不是一个好的解决方案,因为它(相当明显地)遇到了我不想处理的时间问题。例如,如果第一个 goroutine 在另一个 goroutine 生成之前完成,它会过早退出。
以下问题:
从作为 goroutine 开始的函数对其自身进行递归调用是一种有效的方法吗?
cRes在所有派生的 goroutine 完成之前读取结果的惯用方式是什么?我在某处读到,计算完成后通道应该关闭,但我只是想不通在这种情况下如何集成它。
对任何想法都很满意,谢谢!
呼唤远方
紫衣仙女
相关分类