分区列表以执行并行任务

Partitioning lists to execute parallel tasks

我启动了下载多个 URL 的任务。

    Dim downloadTasksQuery As IEnumerable(Of Task(Of Boolean)) =
        From company In companies Select DownloadCompanyFromYahooAsync(company, numberOfDays)
    ' ***Use ToList to execute the query and start the download tasks. 
    Dim downloadTasks As IEnumerable(Of Task(Of Boolean)) = downloadTasksQuery.ToList()

    Await Task.WhenAll(downloadTasks)

companies 列表包含 2000 个 URL。我观察到添加到列表末尾的 URLs 更频繁地超时。我有适当的重试逻辑并正在处理这种超时情况,它会在下一次尝试时下载 URL。但是,我不想仅仅因为URL出现在列表的开头就给予优待。

因此,我们试图思考是否可以分叉 4 个主要任务,将 URL 列表分成 500 个(可能更易于管理),然后使用上面的代码。但是,我无法想出一种方法来介绍它而不必在上面的代码中重写太多。非常感谢任何帮助。

编辑:

更像这样的东西:

    Dim chunkPart As OrderablePartitioner(Of Tuple(Of Integer, Integer)) = Partitioner.Create(1, companies.Count, 500)

    Parallel.ForEach(chunkPart, Sub(chunkRange)
                                    For i As Integer = chunkRange.Item1 To chunkRange.Item2 - 1
                                        Dim downloadTasksQuery As IEnumerable(Of Task(Of Boolean)) =
                                        From company In companies.Skip(chunkRange.Item1).Take((chunkRange.Item2 - chunkRange.Item1) + 1) Select DownloadCompanyFromYahooAsync(company, numberOfDays)
                                        Dim downloadTasks As IEnumerable(Of Task(Of Boolean)) = downloadTasksQuery.ToList()
                                        Await Task.WhenAll(downloadTasks)
                                    Next
                                End Sub

这是对代码的最小改动,但问题是我不能在 Parallel.ForEach.

中使用 Await

有什么建议请修改。

不是 VB.NET 人,但我认为 Stephen Toub 在 implementing a simple ForEachAsync 上的精彩 post 可能对您有所帮助。

他 post 中的一些代码片段,它允许您限制能够并行 运行 的操作数。

public static Task ForEachAsync<T>(this IEnumerable<T> source, int dop, Func<T, Task> body) 
{ 
  return Task.WhenAll( 
    from partition in Partitioner.Create(source).GetPartitions(dop) 
    select Task.Run(async delegate { 
      using (partition) 
        while (partition.MoveNext()) 
          await body(partition.Current); 
  })); 
}

对于您的具体问题,您可以这样使用:

public async Task DownloadForAllCompanies(List<string> companies, int numberOfDays)
{
  await companies.ForEachAsync(4, async company =>
  {
    await DownloadCompanyFromYahooAsync(company, numberOfDays);
  });
}