Parallel.ForEach或Task.WhenAll当涉及异步操作时?

问题描述 投票:-1回答:2

我已经阅读了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
           });
        });
    }

我知道列表不是线程安全的。只是用来证明我的问题的性质。

HttpClientSendAsyncReadAsStringAsync的两种方法都是异步的,因此必须同步调用才能与Parallel.ForEach一起使用。

[Task.WhenAll路线优先吗?我已经尝试了各种性能测试,但似乎找不到任何区别。

c# asynchronous async-await
2个回答
1
投票

我不认为这里主要考虑的是性能。 (它始终是:-),但请继续阅读-在正确的情况下使用正确的构造将确保您获得最佳性能)

Parallel.ForEach视为特殊的ForEach,它并行化各个(同步)任务。尽管您可以通过阻塞将已经进行的异步操作推入其中,但它似乎是人为的和被滥用的-这样您将失去每个任务的异步/等待优势。您从中获得的唯一“好处”是,从代码流的角度来看,它的行为是同步的-直到它产生的所有线程都返回时,它才能完成。

由于您的单个任务已经异步,所以Parallel.ForEach为您提供的是Task.WhenAll的最新功能。


0
投票

在有大量工作要做CPU时,您需要Parallel类。当您必须等待外部世界为您完成的工作时,您需要Task类。Parallel=计算。Task=等待Web服务器,文件系统和数据库响应。不幸的是,内置的.NET库缺少用于异步操作的类似于Parallel.ForEach的简单异步方法,它提供了诸如最大并发度的基本选项。有Task.WhenAll(仅取消),Task.WhenAll(您可以想象的每个选项),两者之间没有任何内容。并发度是这种基本需求,因为当请求溢出时,Web服务器,文件系统和数据库的性能都会很差。

© www.soinside.com 2019 - 2024. All rights reserved.