第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

為了賬號(hào)安全,請(qǐng)及時(shí)綁定郵箱和手機(jī)立即綁定
已解決430363個(gè)問題,去搜搜看,總會(huì)有你想問的

來自函數(shù)的遞歸調(diào)用以 goroutine 和慣用方式開始

來自函數(shù)的遞歸調(diào)用以 goroutine 和慣用方式開始

Go
繁華開滿天機(jī) 2022-12-05 17:17:08
我正在使用 goroutines 實(shí)現(xiàn)一個(gè)(某種)組合回溯算法。我的問題可以表示為具有一定程度/分布的樹,我想訪問每片葉子并根據(jù)所采用的路徑計(jì)算結(jié)果。在給定的級(jí)別上,我想生成 goroutines 來同時(shí)處理子問題,即如果我有一個(gè)度數(shù)為 3 的樹并且我想在級(jí)別 2 之后開始并發(fā),我會(huì)生成 3*3=9 goroutines 繼續(xù)處理子問題并發(fā)。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 語句超時(shí)。我正在尋找一種在所有 goroutine 完成后立即繼續(xù) main() 的方法,即所有子問題都已處理。我已經(jīng)想出了兩種可能的(不受歡迎的/不雅的)解決方案:使用互斥鎖保護(hù)的結(jié)果映射 + 等待組而不是基于通道的方法應(yīng)該可以解決問題,但我很好奇是否有一個(gè)簡(jiǎn)潔的通道解決方案。使用退出通道(int 類型)。每次生成一個(gè) goroutine 時(shí),退出通道都會(huì)得到一個(gè) +1 int,每次在葉子中完成計(jì)算時(shí),它都會(huì)得到一個(gè) -1 int 并且調(diào)用者對(duì)這些值求和。請(qǐng)參閱以下代碼片段,但這不是一個(gè)好的解決方案,因?yàn)樗ㄏ喈?dāng)明顯地)遇到了我不想處理的時(shí)間問題。例如,如果第一個(gè) goroutine 在另一個(gè) goroutine 生成之前完成,它會(huì)過早退出。以下問題:從作為 goroutine 開始的函數(shù)對(duì)其自身進(jìn)行遞歸調(diào)用是一種有效的方法嗎?cRes在所有派生的 goroutine 完成之前讀取結(jié)果的慣用方式是什么?我在某處讀到,計(jì)算完成后通道應(yīng)該關(guān)閉,但我只是想不通在這種情況下如何集成它。對(duì)任何想法都很滿意,謝謝!
查看完整描述

2 回答

?
呼喚遠(yuǎn)方

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è)方面有所幫助:

    1. 編譯器將生成更高效的代碼,因?yàn)橛蟹较虻耐ǖ缹⒂幸粋€(gè)鎖而不是 2 個(gè)。

    2. 函數(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)古老的帖子(在contextstd lib 中存在包之前),我認(rèn)為Parallel digestion你正在尋找解決原始問題的方法。


查看完整回答
反對(duì) 回復(fù) 2022-12-05
?
紫衣仙女

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)

        }

    }

}


查看完整回答
反對(duì) 回復(fù) 2022-12-05
  • 2 回答
  • 0 關(guān)注
  • 113 瀏覽
慕課專欄
更多

添加回答

舉報(bào)

0/150
提交
取消
微信客服

購(gòu)課補(bǔ)貼
聯(lián)系客服咨詢優(yōu)惠詳情

幫助反饋 APP下載

慕課網(wǎng)APP
您的移動(dòng)學(xué)習(xí)伙伴

公眾號(hào)

掃描二維碼
關(guān)注慕課網(wǎng)微信公眾號(hào)