2 回答

TA貢獻(xiàn)1856條經(jīng)驗(yàn) 獲得超11個(gè)贊
閱讀描述和片段我無法準(zhǔn)確理解你想要實(shí)現(xiàn)的目標(biāo),但我有一些我每天使用的頻道的提示和模式并且認(rèn)為有幫助。
該
context
包對(duì)于以安全的方式管理 goroutines 的狀態(tài)非常有幫助。在您的示例中,time.After
用于結(jié)束主程序,但在非主函數(shù)中它可能會(huì)泄漏 goroutines:如果您改為使用context.Context
它并將其傳遞給 gorotuines(它通常傳遞函數(shù)的第一個(gè)參數(shù)),您將能夠控制取消下游呼叫。這簡(jiǎn)單解釋一下。通常的做法是在生成消息并在通道中發(fā)送消息的函數(shù)中創(chuàng)建通道(并返回它們)。同樣的功能應(yīng)該負(fù)責(zé)關(guān)閉通道,例如,
defer close(channel)
當(dāng)它完成寫入時(shí)。這很方便,因?yàn)楫?dāng)通道被緩沖時(shí),即使它仍然有數(shù)據(jù)也可以關(guān)閉它:Go 運(yùn)行時(shí)實(shí)際上會(huì)等到所有消息都被輪詢后再關(guān)閉。對(duì)于無緩沖通道,該函數(shù)將無法通過通道發(fā)送消息,直到通道的讀者準(zhǔn)備好輪詢它,因此無法退出。 這是一個(gè)例子(沒有遞歸)。在此示例中,我們可以close
在緩沖或未緩沖時(shí)使用通道,因?yàn)榘l(fā)送將阻塞直到for := range
在主 goroutine 的通道上從中讀取。 這是相同原理的變體,通道作為參數(shù)傳遞。我們可以與通道
sync.WaitGroup
一起使用,為單個(gè) goroutine 發(fā)出完成信號(hào),并讓“編排”goroutine 知道通道可以關(guān)閉,因?yàn)樗邢⑸a(chǎn)者都已完成向通道發(fā)送數(shù)據(jù)。與第 1 點(diǎn)相同的注意事項(xiàng)適用于close
操作。 這是一個(gè)示例,顯示了 waitGroup 的使用和通道的外部關(guān)閉器。渠道可以有方向!請(qǐng)注意,在示例中,我在將箭頭傳入/外部函數(shù)時(shí)添加/刪除了通道旁邊的箭頭(例如
<-chan string
, 或)。chan<- string
這告訴編譯器,一個(gè)通道在該函數(shù)的范圍內(nèi)分別只讀或?qū)?。這在兩個(gè)方面有所幫助:編譯器將生成更高效的代碼,因?yàn)橛蟹较虻耐ǖ缹⒂幸粋€(gè)鎖而不是 2 個(gè)。
函數(shù)的簽名描述了它是否只會(huì)使用通道寫入(可能
close()
)或讀?。赫?qǐng)記住,range
當(dāng)通道關(guān)閉時(shí),從帶有 a 的通道讀取會(huì)自動(dòng)停止迭代。您可以構(gòu)建通道的通道:
make(chan chan string)
是構(gòu)建處理管道的有效(且有用)構(gòu)造。它的一個(gè)常見用法是扇入 goroutine,它收集一系列 channel-producing goroutines 的多個(gè)輸出。 這是如何使用它們的示例。
本質(zhì)上,回答您最初的問題:
從作為 goroutine 開始的函數(shù)對(duì)其自身進(jìn)行遞歸調(diào)用是一種有效的方法嗎?
如果你真的需要遞歸,最好將它與并發(fā)代碼分開處理:創(chuàng)建一個(gè)專用函數(shù),遞歸地將數(shù)據(jù)發(fā)送到通道,并在調(diào)用者中協(xié)調(diào)通道的關(guān)閉。
在所有派生的 goroutine 完成之前,從 cRes 讀取結(jié)果的慣用方式是什么?我在某處讀到,計(jì)算完成后通道應(yīng)該關(guān)閉,但我只是想不通在這種情況下如何集成它。
一個(gè)很好的參考是Go Concurrency Patterns: Pipelines and cancellation:這是一個(gè)相當(dāng)古老的帖子(在context
std lib 中存在包之前),我認(rèn)為Parallel digestion
你正在尋找解決原始問題的方法。

TA貢獻(xiàn)1839條經(jīng)驗(yàn) 獲得超15個(gè)贊
正如 torek 所提到的,我在 waitgroup 完成等待后剝離了一個(gè)關(guān)閉通道的匿名函數(shù)。還需要一些邏輯,僅在goroutine 生成級(jí)別的遞歸返回后才wg.Done()調(diào)用生成的 goroutine 。
一般來說,我認(rèn)為這是一個(gè)有用的成語(如果我錯(cuò)了請(qǐng)糾正我:))
游樂場(chǎng):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) // edit: code should work without this initial sleep
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)
}
}
}
- 2 回答
- 0 關(guān)注
- 113 瀏覽
添加回答
舉報(bào)