当涉及异步操作时,是Parallel.ForEach还是Task.WhenAll? [英] Parallel.ForEach or Task.WhenAll when involving async operations?

查看:258
本文介绍了当涉及异步操作时,是Parallel.ForEach还是Task.WhenAll?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已阅读遵循密切相关的线程,但我想问一个更具体的问题.

I've read the following closely related thread, but I'd like to ask about a more specific thing.

如果我们需要异步运行Tasks/method,并且那些任务本身运行其他任务/等待其他任务,则首选哪种变体-Parallel.ForEach或Task.WhenAll?我将在下面的代码中进行演示:

If we need to run Tasks/methods asynchronously, and those tasks themselves run other tasks/await other tasks, which variant is prefered - Parallel.ForEach, or Task.WhenAll? I will demonstrate with some code below:

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一起使用:

Used with 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()编写:

Or, all of the above written in a 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
           });
        });
    }

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

I'm aware that Lists are not thread safe. It's just something to demonstrate the nature of my question.

HttpClient(SendAsyncReadAsStringAsync)的两个方法都是异步的,因此必须同步调用才能与Parallel.ForEach一起使用.

Both methods of HttpClient (SendAsync and ReadAsStringAsync) are asynchronous, and as such must be called synchronously in order to work with Parallel.ForEach.

相对于Task.WhenAll路线,这更可取吗?我已经尝试了各种性能测试,但似乎找不到任何区别.

Is that preferred over the Task.WhenAll route? I've tried various performance tests, and I can't seem to find a difference.

推荐答案

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

I don't think the main consideration here is performance. (It always is :-) but read on - using the correct construct in the correct case will guarantee you the best performance)

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

Think of Parallel.ForEach as a special ForEach which is parallelizing the individual (synchronous) tasks. While you could shove already asynchronous operations in it (by blocking), it seems contrived and misused - you will lose the async/await benefits of each tasks by doing so. The only "benefit" that you get out of it is that its behavior from the stand point of view of your code flow is synchronous - it will not complete until all threads it spawned return.

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

Since your individual tasks are already async, it is the latest feature of the Parallel.ForEach that Task.WhenAll gives you.

这篇关于当涉及异步操作时,是Parallel.ForEach还是Task.WhenAll?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆