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

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

Go例程泄漏在哪里?

Go例程泄漏在哪里?

Go
搖曳的薔薇 2021-09-27 10:52:45
我正在嘗試同時運行多個任務,并在出現任何錯誤時立即返回,而無需等待所有例程返回。代碼如下所示。我已經去除了噪音以使其更容易消化,但如果泄漏不明顯,我可以發布完整的代碼。值得注意的是,我正在谷歌應用引擎上部署它。我無法在我的機器上重現泄漏,但是當我在// Consume the results評論后替換并發時,應用程序運行良好,但我不明白為什么,因為代碼對我來說是正確的。package mainimport "fmt"import "sync"import "errors"func main() {    indexes := []int{1, 2, 3, 4, 5, 6, 7}    devCh := make(chan int, 7)    stopCh := make(chan struct{})    errCh := make(chan error, 7)    var wg sync.WaitGroup    go func() {        for _, sub := range indexes {            wg.Add(1)            go func(sub int) {                defer wg.Done()                // some code which creates other                // wait groups and spans other go routines                // handle errors                if sub == 99 { // unreachable                     errCh <- errors.New("new error")                }            }(sub)            select {            // If there is any error we better stop the            // loop            case <-stopCh:                return            default:            }            devCh <- sub        }        wg.Wait()        close(devCh)    }()    // Consume the results    var results []int    var wt sync.WaitGroup    wt.Add(1)    go func() {        defer wt.Done()        for s := range devCh {            results = append(results, s)        }        return    }()    done := make(chan struct{})    go func() {        wt.Wait()        close(done)    }()L:    for {        select {        case err := <-errCh:            fmt.Printf("error was %v", err)            close(stopCh)            return        case <-done:            break L        default:        }    }    fmt.Printf("all done, %v", results)}
查看完整描述

1 回答

?
青春有我

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

一個循環只重復非阻塞檢查直到它成功會導致難以診斷的問題(至少,它會過度使用 CPU);使用阻塞檢查可以修復它。


我不太確定你的案子的細節。我寫了一個像你這樣的循環,它在 Playground 上始終掛起“進程花費太長時間”,但是當我在本地運行它時,它確實完成了。


正如我所評論的,我的目標是更簡單的設計。


Go 僅對運行中的 goroutine 具有有限的搶占權:運行中的線程僅在發生阻塞操作(如 I/O 或通道操作或等待獲取鎖)時才將控制權交給 goroutine 調度程序。


因此GOMAXPROCS=1,如果(一個)正在運行的線程開始循環,則其他任何東西都不一定有機會運行。


for { select { ...default: } }因此,A可以開始循環檢查通道中的項目,但永遠不會放棄對主線程的控制,以便另一個 goroutine 可以寫入項目。當GOMAXPROCS超過 1 時,其他代碼無論如何都會運行,但不會像 App Engine(或 Playground)上的 1 那樣運行。行為不僅取決于GOMAXPROCS,還取決于哪個 goroutine 首先運行,這不一定定義。


為了避免這種情況,刪除default:soselect是一個阻塞操作,當調度程序無法接收項目時,它會產生阻塞操作,允許其他代碼運行。您可以將其推廣到其他可能循環進行非阻塞檢查的情況;當阻塞調用不會時,它們中的任何一個都可以使資源忙于不斷地重新檢查。當GOMAXPROCS>1或運行時的有限搶占拯救您時,輪詢(如調用重復檢查)仍然可以比阻塞消耗更多的 CPU。


例如,這在 Playground 上因“過程花費太長時間”而失敗,盡管煩人的是它在我的機器上可靠地完成:


package main


import "fmt"


func main() {

    c := make(chan struct{})

    go func() { c <- struct{}{} }()

    for {

        select {

        case <-c:

            fmt.Println("success")

            return

        default:

        }

    }

我不知道是否還有其他問題,但與樣本相似的模式的掛起值得注意。


查看完整回答
反對 回復 2021-09-27
  • 1 回答
  • 0 關注
  • 181 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

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

幫助反饋 APP下載

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

公眾號

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