C#Parallel.ForEach和Task.WhenAll有时返回的值比假设的要少
c#
我有这个:
Parallel.ForEach(numbers, (number) =>
{
var value = Regex.Replace(number, @"s+", "%20");
tasks.Add(client.GetAsync(url + value));
});
await Task.WhenAll(tasks).ConfigureAwait(false);
foreach (var task in tasks)
{
...
}
有时在到达 foreach(任务中的 var 任务)时返回较少的任务,但在几次请求后,开始返回所有任务。
我已将 ConfigureAwait 更改为 true,但有时仍会返回较少的任务。
顺便说一句,我使用 Parallel.ForEach,因为每个 client.GetAsync(url + value) 都是对外部 api 的请求,其特殊性在于其 99% 的请求的延迟 SLA 低于 1s
你们能解释一下为什么它有时会返回较少的任务吗?
有没有办法保证总是返回所有任务?
谢谢
回答
有没有办法保证总是返回所有任务?
评论中的几个人指出你应该这样做,假设它numbers是一个非线程安全列表:
foreach(var number in numbers)
{
var value = Regex.Replace(number, @"s+", "%20");
tasks.Add(client.GetAsync(url + value));
}
await Task.WhenAll(tasks).ConfigureAwait(false);
foreach (var task in tasks)
{
...
}
并行创建下载任务似乎没有任何显着的好处;这发生得非常快。等待下载完成是在WhenAll
ps; 有多种更复杂的方法可以为 URL 转义数据,但是如果您特别想将任何类型的空格转换为 %20,我想用正则表达式来做是有意义的。
编辑; 你问什么时候使用 Parallel ForEach,我会说“一般不要,因为你必须更加小心你使用它的上下文”,但是如果你让 Parallel.ForEach 做更多同步工作,这可能是有道理的:
Parallel.ForEach(numbers, number =>
{
var value = Regex.Replace(number, @"s+", "%20");
var r = client.Get(url + value));
//do something meaningful with r here, i.e. whatever ... is in your foreach (var task in tasks)
});
但是请注意,如果您出于协调目的从主体内部对某些共享事物执行更新,则它需要是线程安全的
- No need for concurrent collection; nothing is happening with it concurrently; it happens sequentially in the `foreach`. The most critical part is that you don't `await` in the foreach (which you don't, but I'm saying "don't be tempted to add it"), otherwise the IO of the requests *will* be done sequentially
- @pinkfloydx33 I CW'd it anyway, as it was really only visualizing what everyone else was saying so I didn't feel like it was "my answer".. but thanks! 🙂
THE END
二维码