在sync.WaitGroup goroutine中写入chan

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

我正在从 API 端点获取项目列表。然后,对于每个项目,我都会发出另一个 API 请求以获取有关单个项目的数据。

我无法同时对每个项目发出第二个 API 请求,因为我的 API 令牌有速率限制,如果我同时发出太多请求,我会受到限制。

但是,初始 API 响应数据可以拆分为多个页面,这使我能够同时处理多页数据。

经过一些研究,下面的代码正是我想要的:

func main() {
    // pretend paginated results from initial API request
    page1 := []int{1, 2, 3}
    page2 := []int{4, 5, 6}
    page3 := []int{7, 8, 9}
    pages := [][]int{page1, page2, page3}

    results := make(chan string)

    var wg sync.WaitGroup
    for i := range pages {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            for j := range pages[i] {
                // simulate making additional API request and building the report
                time.Sleep(500 * time.Millisecond)

                result := fmt.Sprintf("Finished creating report for %d", pages[i][j])
                results <- result
            }

        }(i)
    }

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

    for result := range results {
        fmt.Println(result)
    }
}

我想了解为什么它能发挥作用:

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

我的第一次尝试没有成功——我以为我可以在

wg.Wait()
之后在通道上进行范围调整,并且我会在结果写入
results
通道时读取结果。

func main() {
    // pretend paginated results from initial API request
    page1 := []int{1, 2, 3}
    page2 := []int{4, 5, 6}
    page3 := []int{7, 8, 9}
    pages := [][]int{page1, page2, page3}

    results := make(chan string)

    var wg sync.WaitGroup
    for i := range pages {
        wg.Add(1)
        go func(i int) {
            defer wg.Done()
            for j := range pages[i] {
                // simulate making additional API request and building the report
                time.Sleep(500 * time.Millisecond)

                result := fmt.Sprintf("Finished creating report for %d", pages[i][j])
                results <- result
            }

        }(i)
    }

    // does not work
    wg.Wait()
    close(results)

    for result := range results {
        fmt.Println(result)
    }
}
go goroutine waitgroup
1个回答
0
投票

在你的第一次尝试中:

  1. 主 goroutine 使 3 个 goroutine 将值放入结果通道中。
  2. 主协程等待所有协程完成。
  3. 其中一个 goroutine 在结果通道中放入一个值并填充通道(通道大小为 1 个字符串)。
  4. 所有三个 goroutine 现在无法再将值放入结果通道并进入睡眠状态,直到结果通道被释放。
  5. 所有 goroutine 都在睡觉。你陷入了僵局。

在你的第二次尝试中:

  1. 主 goroutine 包含 4 个 goroutine。
  2. 3 个 goroutine 将值放入结果通道中。
  3. 其他 Goroutine(我将其称为第 4 个)等待这 3 个 Goroutine 结束。
  4. 同时主协程等待结果通道中的值(for循环)
  5. 在这种情况下,如果其中一个 goroutine 在结果通道中放入了一个值,则阻塞了其余的三个 goroutine;主 Goroutine 将值从结果通道中取出,从而解除对其他 Goroutine 的阻塞。
  6. 因此所有 3 个 goroutine 都放入各自的值并结束
  7. 然后第 4 个 goroutine 关闭通道
  8. 主 Goroutine 结束其 for 循环。
© www.soinside.com 2019 - 2024. All rights reserved.