带有 WaitGroup 的 Goroutine 会执行相同的代码两次

问题描述 投票:0回答:1

我相对较新,并且在一些并发代码方面遇到了一些问题:

由于某种原因,else 块执行了两次,程序出现恐慌,并显示以下消息:

panic: close of closed channel
,因为
extractedCh
已在第一次关闭。

func (ex Extractor) Extract(pageCh chan *types.ScrapedData, extractedCh chan *types.ScrapedData) {
    log.Info().Msg("Extracting data...")

    var wg sync.WaitGroup

    for {
        page, more := <-pageCh
        if more {
            wg.Add(1)

            go func() {
                defer wg.Done()
                worker, err := ex.getWorker(page)
                if err != nil {
                    log.Error().Err(err).Msg("Error creating worker")
                } else {
                    worker.Extract(extractedCh)
                }
            }()

        } else {
            log.Info().Msg("WebPage channel closed: waiting for waitgroup")
            wg.Wait()

            log.Info().Msg("WaitGroup finished closed -- closing extractedCh")
            close(extractedCh)
        }

    }

}

Extract 函数仅在一个 Goroutine 中运行 - 所以我不确定为什么 else 块会执行两次并在

extractedCh
已经关闭时尝试关闭它。

解决方法是什么:我是否需要重组我的代码,或者只是在尝试关闭通道之前检查通道是否已关闭?

go channel goroutine
1个回答
1
投票

else 块会第二次执行,因为当

pageCh
关闭时,else 块不会跳出循环。通过在 else 块的末尾添加
return
语句来修复。更好的是,像这样重写代码:

func (ex Extractor) Extract(pageCh chan *types.ScrapedData, extractedCh chan *types.ScrapedData) {
    log.Info().Msg("Extracting data...")
    var wg sync.WaitGroup
    for page := range pageCh {
        wg.Add(1)
        page := page // delete this line if compiling for Go 1.22 or later
        go func() {
            defer wg.Done()
            worker, err := ex.getWorker(page)
            if err != nil {
                log.Error().Err(err).Msg("Error creating worker")
            } else {
                worker.Extract(extractedCh)
            }
        }()
    }
    log.Info().Msg("WebPage channel closed: waiting for waitgroup")
    wg.Wait()
    log.Info().Msg("WaitGroup finished closed -- closing extractedCh")
    close(extractedCh)
}

pageCh
关闭时,for 循环退出。

可能引起混乱的一件事是这一行:

    page, more := <-pageCh

通道接收运算符的第二个返回值意味着“收到一个值”,而不是“期望更多值”。如果第二个返回值是

false
,意思是“没有收到任何值”,那么通道上的接收将永远在第二个返回值中返回
false

© www.soinside.com 2019 - 2024. All rights reserved.