你如何使用asParallel与async和await关键字?

Ros*_*gan 16 c# asynchronous

我正在查看异步的某个示例代码,并注意到它实现方式的一些问题.在查看代码时,我想知道使用并行循环遍历列表是否更有效,而不是正常循环遍历列表.

据我所知,性能差异很小,两者都占用了每个处理器,两者都在相同的时间内完成.

这是第一种方式

var tasks= Client.GetClients().Select(async p => await p.Initialize());
Run Code Online (Sandbox Code Playgroud)

这是第二个

var tasks = Client.GetClients().AsParallel().Select(async p => await p.Initialize());
Run Code Online (Sandbox Code Playgroud)

假设两者之间没有区别,我是否正确?

完整的程序可以在下面找到

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            RunCode1();
            Console.WriteLine("Here");
            Console.ReadLine();

            RunCode2();
            Console.WriteLine("Here");

            Console.ReadLine();

        }

        private async static void RunCode1()
        {
            Stopwatch myStopWatch = new Stopwatch();
            myStopWatch.Start();

            var tasks= Client.GetClients().Select(async p => await p.Initialize());

            Task.WaitAll(tasks.ToArray());
            Console.WriteLine("Time ellapsed(ms): " + myStopWatch.ElapsedMilliseconds);
            myStopWatch.Stop();
        }
        private async static void RunCode2()
        {
            Stopwatch myStopWatch = new Stopwatch();
            myStopWatch.Start();
            var tasks = Client.GetClients().AsParallel().Select(async p => await p.Initialize());
            Task.WaitAll(tasks.ToArray());
            Console.WriteLine("Time ellapsed(ms): " + myStopWatch.ElapsedMilliseconds);
            myStopWatch.Stop();
        }
    }
    class Client
    {
        public static IEnumerable<Client> GetClients()
        {
            for (int i = 0; i < 100; i++)
            {
                yield return new Client() { Id = Guid.NewGuid() };
            }
        }

        public Guid Id { get; set; }

        //This method has to be called before you use a client
        //For the sample, I don't put it on the constructor
        public async Task Initialize()
        {
            await Task.Factory.StartNew(() =>
                                      {
                                          Stopwatch timer = new Stopwatch();
                                          timer.Start();
                                          while(timer.ElapsedMilliseconds<1000)
                                          {}
                                          timer.Stop();

                                      });
            Console.WriteLine("Completed: " + Id);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*ary 17

应该有很少的可辨别的差异.

在你的第一个案例中:

var tasks = Client.GetClients().Select(async p => await p.Initialize());
Run Code Online (Sandbox Code Playgroud)

执行线程将(一次一个)开始Initialize为客户端列表中的每个元素执行.Initialize立即将方法排队到线程池并返回未完成的Task.

在你的第二种情况:

var tasks = Client.GetClients().AsParallel().Select(async p => await p.Initialize());
Run Code Online (Sandbox Code Playgroud)

执行线程将分叉到线程池并(并行)开始Initialize为客户端列表中的每个元素执行.Initialize具有相同的行为:它立即将方法排队到线程池并返回.

这两个时序几乎完全相同,因为您只是并行化少量代码:将方法排队到线程池并返回未完成的代码Task.

如果Initialize在第一次之前做了一些更长的(同步)工作await,那么使用它可能是有意义的AsParallel.

请记住,所有async方法(和lambdas)都开始同步执行(请参阅官方常见问题解答我自己的介绍).


cas*_*One 6

有一个单一的主要区别。

在下面的代码中,您自己负责执行分区。换句话说,您正在从调用返回的Task每个项目创建一个对象:IEnumerable<T>GetClients()

var tasks= Client.GetClients().Select(async p => await p.Initialize());
Run Code Online (Sandbox Code Playgroud)

在第二个中,对AsParallel内部的调用将使用Task实例来执行 的分区,IEnumerable<T> 并且您将获得Task从 lambda 返回的初始值async p => await p.Initialize()

var tasks = Client.GetClients().AsParallel().
    Select(async p => await p.Initialize());
Run Code Online (Sandbox Code Playgroud)

最后,在这里使用async/ 并没有真正做任何事情await。当然,编译器可能会对此进行优化,但您只是在等待一个返回 a 的方法,Task然后返回一个通过 lambda 不执行任何操作的延续。也就是说,由于对 的调用Initialize已经返回 a Task,最好保持简单并执行以下操作:

var tasks = Client.GetClients().Select(p => p.Initialize());
Run Code Online (Sandbox Code Playgroud)

这将为您返回Task实例序列。