【问题标题】:Understanding the logic of WaitGroups理解 WaitGroups 的逻辑
【发布时间】:2020-03-21 11:38:17
【问题描述】:

我想了解我围绕 WaitGroups 的逻辑是否正确,并看看是否有更有效的方式来构建我的代码。目的是尽可能快地执行任务。

我的代码填充了一个通过标准输入填充的_urls 频道。然后我启动了两个 WaitGroup,一个从这个 _urls 通道读取,另一个从一个 _downloads 通道读取,它是从第一个 WaitGroup 中的 goroutine 提供的。

基本上代码如下所示:

    // declare channels
    _urls := make(chan string)
    _downloads := make(chan string)

    // first waitgroup with 2 goroutines
    var wg sync.WaitGroup
    for i := 0; i < concurrency; i++ {

        wg.Add(2)

        go func() {
            defer wg.Done()
            for url := range _urls {
                // perform GET request and inspect the responseBody    
            }

        }()

        go func() {
            defer wg.Done()
            for url := range _urls {
                // perform a HEAD request to look for a certain file
                // if the file exists, send to the _downloads channel                
                _downloads <- url
            }
        }()
    }

    // second waitgroup with 1 goroutine
    var dwg sync.WaitGroup
    for i := 0; i < concurrency; i++ {

        dwg.Add(1)

        go func() {
            defer wg.Done()
            for url := range _downloads {
                // perform the download
            }
        }()
    }  

我关心的是这是否是提供_downloads 频道的有效方式,还是只在第一个 WaitGroup 中执行下载是否更有意义。

【问题讨论】:

  • 我认为在您的示例中根本没有任何理由使用等待组。你添加它们的原因是什么?您也永远不会等待它们完成,因此在您的示例中它们是无操作的。
  • @Flimzy tbh 我在一些博客和其他 github 存储库中看到了类似的代码结构,因此假设它一定是正确的......
  • 这可能是正确的,因为他们正在解决的问题。这并不意味着它适合您正在解决的问题。
  • 我开始写一个详细的答案,但您的代码中没有足够的信息来提出正确的结构。完全不清楚您的前两个 goroutine(分别执行 GET 和 HEAD)如何相互关联,或与最终的 goroutine(执行下载)相关。但我认为这里根本没有理由使用等待组。
  • sync.WaitGroups 让您等到一定数量的独立事情发出信号完成。这就是关于 WaitGroups 的全部知识。它是关于等到一组事情完成。这是“最好的”、“最快的”还是“最惯用的”,很大程度上取决于实际问题。

标签: go concurrency goroutine


【解决方案1】:

我对工作池模式https://gobyexample.com/worker-pools 做了类似的事情。如果您希望最大化并发性,这可能是正确的方向。

它使用 go 接口抽象作业,因此它可能是 HEAD、GET、Download 或其他任何在未来发生的有意义的事情。调度程序将作业发送到管理工作池并将结果发回的调度程序。

这是README 和代码的链接。

它使用等待组来跟踪活跃工作人员的数量,而不是工作。工作人员执行for {} loop 并且仅在他们从完成的通道中读取为真时退出。在这种情况下,使用等待组是为了正常关闭。在您的示例中,许多工作人员可能正在进行长时间下载。因此,您的关闭逻辑可能会在关闭之前等待剩余 N 个作业。

这对您的用例来说可能有点过头了。

【讨论】:

  • 没问题。仅供参考,根据您的工作负载,使用 Go 通道可能过于受限,因为它要求所有工作人员都位于单个计算节点上的单个进程空间中。我正在计划使用 Kafka 作为消息总线的第二个版本。这将允许多个进程和计算节点。
猜你喜欢
  • 1970-01-01
  • 2021-01-09
  • 1970-01-01
  • 2010-10-28
  • 2017-07-13
  • 2023-03-27
  • 1970-01-01
  • 2014-10-03
  • 2022-01-09
相关资源
最近更新 更多