我已经阅读了following紧密相关的线程,但是我想问一个更具体的问题。
如果我们需要异步运行Tasks / method,并且那些任务本身运行其他任务/等待其他任务,则首选哪种变体-Parallel.ForEach或Task.WhenAll?我将在下面的代码中进行演示:
public async Task SomeWorker(string param1, HttpClient client, List<FillMeUp> emptyCollection)
{
HttpRequestMessage message = new HttpRequestMessage();
message.Method = HttpMethod.Get;
message.Headers.Add("someParam", param1);
message.RequestUri = new Uri("https://www.somesite.me");
var requestResponse = await client.SendAsync(message).ConfigureAwait(false);
var content = await requestResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
emptyCollection.Add(new FillMeUp()
{
Param1 = param1
});
}
与WhenAll一起使用:
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Accept", "application/json");
List<FullCollection> fullCollection = GetMyFullCollection();
List<FillMeUp> emptyCollection = new List<FillMeUp>();
List<Task> workers = new List<Task>();
for (int i = 0; i < fullCollection.Count; i++)
{
workers.Add(SomeWorker(fullCollection[i].SomeParam, client, emptyCollection));
}
await Task.WhenAll(workers).ConfigureAwait(false);
// Do something below with already completed tasks
}
或者,以上所有内容均以Parallel.ForEach():
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Accept", "application/json");
List<FullCollection> fullCollection = GetMyFullCollection();
List<FillMeUp> emptyCollection = new List<FillMeUp>();
Parallel.ForEach<FullCollection>(fullCollection, (fullObject) =>
{
HttpRequestMessage message = new HttpRequestMessage();
message.Method = HttpMethod.Get;
message.Headers.Add("someParam", fullObject.SomeParam);
message.RequestUri = new Uri("https://www.somesite.me");
var requestResponse = client.SendAsync(message).GetAwaiter().GetResult();
var content = requestResponse.Content.ReadAsStringAsync().GetAwaiter().GetResult();
emptyCollection.Add(new FillMeUp()
{
Param1 = fullObject.SomeParam
});
});
}
我知道列表不是线程安全的。只是用来证明我的问题的性质。
HttpClient
(SendAsync
和ReadAsStringAsync
的两种方法都是异步的,因此必须同步调用才能与Parallel.ForEach
一起使用。
[Task.WhenAll
路线优先吗?我已经尝试了各种性能测试,但似乎找不到任何区别。
我不认为这里主要考虑的是性能。 (它始终是:-),但请继续阅读-在正确的情况下使用正确的构造将确保您获得最佳性能)
将Parallel.ForEach
视为特殊的ForEach
,它并行化各个(同步)任务。尽管您可以通过阻塞将已经进行的异步操作推入其中,但它似乎是人为的和被滥用的-这样您将失去每个任务的异步/等待优势。您从中获得的唯一“好处”是,从代码流的角度来看,它的行为是同步的-直到它产生的所有线程都返回时,它才能完成。
由于您的单个任务已经异步,所以Parallel.ForEach
为您提供的是Task.WhenAll
的最新功能。
在有大量工作要做CPU时,您需要Parallel
类。当您必须等待外部世界为您完成的工作时,您需要Task
类。Parallel
=计算。Task
=等待Web服务器,文件系统和数据库响应。不幸的是,内置的.NET库缺少用于异步操作的类似于Parallel.ForEach
的简单异步方法,它提供了诸如最大并发度的基本选项。有Task.WhenAll
(仅取消),Task.WhenAll
(您可以想象的每个选项),两者之间没有任何内容。并发度是这种基本需求,因为当请求溢出时,Web服务器,文件系统和数据库的性能都会很差。