分区列表以执行并行任务
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);
});
}
我启动了下载多个 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);
});
}