我正在使用 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):
fmt.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 生成之前完成,它會過早退出。
for {
select {
case q := <-cRunningRoutines:
runningRoutines = q
if runningRoutines == 0 {
fmt.Println("Calculation complete")
return res
}
// ...same cases as above
}
游樂場:https ://go.dev/play/p/9jzeCvl8Clj
以下問題:
- 從作為 goroutine 開始的函式對自身進行遞回呼叫是一種有效的方法嗎?
cRes在所有生成的 goroutine 完成之前讀取結果的慣用方式是什么?我在某處讀到,計算完成后應該關閉通道,但在這種情況下,我只是不知道如何集成它。
對任何想法感到高興,謝謝!
uj5u.com熱心網友回復:
正如 torek 所提到的,我分離了一個匿名函式,在等待組完成等待后關閉通道。wg.Done()只有在goroutine 生成級別的遞回回傳之后,還需要一些關于呼叫生成的 goroutine 的邏輯。
一般來說,我認為這是一個有用的成語(如果我錯了,請糾正我:))
游樂場:https ://go.dev/play/p/bQjHENsZL25
func main() {
cRes := make(chan string, 100)
numLevels := 3
spread := 3
startConcurrencyAtLevel := 2
var wg sync.WaitGroup
nTree("", numLevels, spread, startConcurrencyAtLevel, cRes, &wg)
go func() {
time.Sleep(1 * time.Second)
wg.Wait()
close(cRes)
}()
for r := range cRes {
fmt.Println(r)
}
fmt.Println("Done!")
}
func nTree(path string, maxLevels int, spread int, startConcurrencyAtLevel int, cRes chan string, wg *sync.WaitGroup) {
if len(path) == maxLevels {
// some longer running task here associated with the found path
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, wg)
} else {
nTree(nextPath, maxLevels, spread, startConcurrencyAtLevel, cRes, wg)
}
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/466892.html
