我正在查看异步的某个示例代码,并注意到它实现方式的一些问题.在查看代码时,我想知道使用并行循环遍历列表是否更有效,而不是正常循环遍历列表.
据我所知,性能差异很小,两者都占用了每个处理器,两者都在相同的时间内完成.
这是第一种方式
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)都开始同步执行(请参阅官方常见问题解答或我自己的介绍).
有一个单一的主要区别。
在下面的代码中,您自己负责执行分区。换句话说,您正在从调用返回的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实例序列。
| 归档时间: |
|
| 查看次数: |
7776 次 |
| 最近记录: |