亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

“所有 goroutine 都在睡覺 - 死鎖!”的情況我不明白為什么

“所有 goroutine 都在睡覺 - 死鎖!”的情況我不明白為什么

Go
慕的地10843 2023-08-14 16:21:35
一個典型的案例all goroutines are asleep, deadlock!,但無法弄清楚我正在解析維基詞典 XML 轉儲來構建單詞數據庫。我將每篇文章的文本的解析推遲到一個 Goroutine,希望它能加快這個過程。它有 7GB,在我的機器上串行執行時,處理時間不到 2 分鐘,但如果我可以利用所有內核,為什么不呢。一般來說,我是線程新手,遇到錯誤all goroutines are asleep, deadlock!。這是怎么回事?這可能根本沒有性能,因為它使用無緩沖的通道,因此所有 goroutine 實際上最終都會串行執行,但我的想法是學習和理解線程,并衡量不同替代方案所需的時間:無緩沖通道不同大小的緩沖通道一次只調用盡可能多的 goroutineruntime.NumCPU()我的偽代碼代碼摘要:
查看完整描述

3 回答

?
三國紛爭

TA貢獻1804條經驗 獲得超7個贊

while tag := xml.getNextTag() {

    wg.Add(1)

    go parseTagText(chan, wg, tag.text)


    // consume a channel message if available

    select {

    case msg := <-chan:

        // do something with msg            

    default:

    }

}

// reading tags finished, wait for running goroutines, consume what's left on the channel

for msg := range chan {

    // do something with msg

}

// Sometimes this point is never reached, I get a deadlock

wg.Wait()


----


func parseTagText(chan, wg, tag.text) {

    defer wg.Done()

    // parse tag.text

    chan <- whatever // just inform that the text has been parsed

}

完整代碼: https:

//play.golang.org/p/0t2EqptJBXE


查看完整回答
反對 回復 2023-08-14
?
慕萊塢森

TA貢獻1810條經驗 獲得超4個贊

在 Go Playground 上的完整示例中,您:

  • 創建一個通道(第 39 行,results := make(chan langs))和一個等待組(第 40 行,var wait sync.WaitGroup)。到目前為止,一切都很好。

  • 循環:在循環中,有時會衍生出一個任務:

                if ...various conditions... {
                    wait.Add(1) 
                                   go parseTerm(results, &wait, text)
                }
  • 在循環中,有時會從通道進行非阻塞讀取(如您的問題所示)。這里也沒有問題。但...

  • 在循環結束時,使用:

    for res := range results {
        ...
    }

    在所有作家完成后,無需精確地調用close(results)一個地方。此循環使用從通道的阻塞讀取。只要某個 writer goroutine 仍在運行,阻塞讀取就可以阻塞,而不會導致整個系統停止,但是當最后一個 writer 完成寫入并退出時,就沒有剩余的 writer goroutine 了。任何其他剩余的 goroutine 可能會拯救你,但沒有。

由于您使用var wait正確(在正確的位置添加 1,并Done()在 writer 中的正確位置調用),解決方案是再添加一個 goroutine,它將拯救您:

go func() {
    wait.Wait()
        close(results)
}()

您應該在進入循環之前關閉這個救援 goroutine for res := range results。(如果您更早地將其分離,它可能會wait很快看到變量計數減至零,就在它通過分離另一個 再次計數之前parseTerm。)

這個匿名函數將阻塞在wait變量的Wait()函數中,直到最后一個 writer Goroutine 調用了 Final wait.Done(),這將解除對這個Goroutine 的阻塞。然后這個 goroutine 將調用close(results),這將安排goroutinefor中的循環main完成,從而解鎖該 goroutine。當這個 goroutine(救援者)返回并因此終止時,不再有救援者,但我們不再需要任何救援者。

(這個主代碼然后wait.Wait()不必要地調用:因為直到新的goroutine中的已經解除阻塞for才終止,我們知道下一個將立即返回。所以我們可以放棄第二個調用,盡管保留它是無害的。)wait.Wait()wait.Wait()


查看完整回答
反對 回復 2023-08-14
?
慕仙森

TA貢獻1827條經驗 獲得超8個贊

問題是沒有任何東西可以關閉結果通道,但范圍循環僅在關閉時退出。我簡化了您的代碼來說明這一點并提出了一個解決方案 - 基本上使用 goroutine 中的數據:


// This is our producer

func foo(i int, ch chan int, wg *sync.WaitGroup) {

    defer wg.Done()

    ch <- i

    fmt.Println(i, "done")

}

// This is our consumer - it uses a different WG to signal it's done

func consumeData(ch chan int, wg *sync.WaitGroup) {

    defer wg.Done()

    for x := range ch {

        fmt.Println(x)

    }

    fmt.Println("ALL DONE")

}


func main() {

    ch := make(chan int)

    wg := sync.WaitGroup{}

    // create the producers

    for i := 0; i < 10; i++ {

        wg.Add(1)

        go foo(i, ch, &wg)

    }

    // create the consumer on a different goroutine, and sync using another WG

    consumeWg := sync.WaitGroup{}

    consumeWg.Add(1)

    go consumeData(ch,&consumeWg)


    wg.Wait()  // <<<< means that the producers are done

    close(ch) // << Signal the consumer to exit

    consumeWg.Wait() // << Wait for the consumer to exit

}


查看完整回答
反對 回復 2023-08-14
  • 3 回答
  • 0 關注
  • 202 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號