可能重复:
Parallel.ForEach VS Task.Factory.StartNew
我需要运行在约1000任务ThreadPool
每个晚上(数量可能在未来拓展)。 每个任务都执行一个长期运行的操作(从Web服务读取数据),而不是CPU密集型 。 Async I/O
是不是这个特定用例的选项。
给定一个IList<string>
的参数,我需要DoSomething(string x)
我想下面的两个选项之间进行挑选:
IList<Task> tasks = new List<Task>();
foreach (var p in parameters)
{
tasks.Add(Task.Factory.StartNew(() => DoSomething(p), TaskCreationOptions.LongRunning));
}
Task.WaitAll(tasks.ToArray());
要么
Parallel.ForEach(parameters, new ParallelOptions {MaxDegreeOfParallelism = Environment.ProcessorCount*32}, DoSomething);
哪种选择更好,为什么?
注意 :
答案应该包括的使用量之间的比较TaskCreationOptions.LongRunning
和MaxDegreeOfParallelism = Environment.ProcessorCount * SomeConstant
。
也许你没有意识到这一点,但在成员Parallel
类只是各地(复杂)的包装Task
的对象。 在你想知道的情况下, Parallel
类创建Task
与对象TaskCreationOptions.None
。 然而, MaxDegreeOfParallelism
就不管影响这些任务对象传递给任务对象的构造是什么创建选项。
TaskCreationOptions.LongRunning
给人一种“暗示”到底层TaskScheduler
,这可能与线程的超额表现得更好。 超额认购有利于高延迟的线程,例如I / O,因为它会分配多个线程(是线程,而不是任务),以单核,使其始终,而不是等着事做,操作完成,而线程处于等待状态。 在TaskScheduler
使用的ThreadPool
, 它将在自己的专用线程(在这里你有每个任务一个线程的唯一情况)运行LongRunning任务,否则将正常运行,与调度和工作窃取(真的,反正你想要的这里)
MaxDegreeOfParallelism
控制并发操作运行的数量。 它类似于指定该数据将被分成和处理从paritions的最大数量。 如果TaskCreationOptions.LongRunning
能够被指定,这一切会做将是限制在某一时间运行的任务数,类似TaskScheduler
其最大并发级别设置为值, 类似这样的例子 。
您可能希望在Parallel.ForEach
。 然而,增加MaxDegreeOfParallelism
等于这么高的数字实际上并不能保证会有这一次可以运行多个线程,因为任务将仍然由控制ThreadPoolTaskScheduler
。 这调度程序将线程数运行一次,以最小的量可能,我想是这两种方法之间的最大区别。 你可以写(并指定)自己TaskScheduler
,将模仿的并行行为的最大程度,并有两全其美的,但我怀疑的东西,你有兴趣做。
我的猜测是,根据延迟和你需要做实际的请求的数量,使用的任务将执行在许多(?)的情况下更好,虽然拉闸使用更多的内存,同时并行将在资源使用更加一致。 当然,异步I / O将执行无情无义比这两个选项好,但我明白,你不能这样做,因为您使用旧版库。 所以,不幸的是,你会用表现平平卡住不管那些你选择的是哪一个。
一个真正的解决办法是找出一种方法,使异步I / O发生; 因为我不知道这个情况,我不认为我能比这更有益。 你的程序(读线程)将继续执行,内核将等待I / O操作完成(这也被称为使用I / O完成端口)。 因为线程不处于等待状态,运行时可以用较少的线程,这通常在芯的数目和线程的数目之间的最佳关系结束更多的工作。 添加更多的线程,就像我希望它会,不等于(因为像上下文切换的事情实际上,它往往可以损害性能)更好的性能。
然而,这整个的答案是在确定你的问题最终答案没用,但我希望它会给你一些必要的方向。 你不会知道,直到你个人资料该怎么执行得更好。 如果你不尝试他们两个(我要澄清,我的意思是任务没有LongRunning选项,让调度处理线程切换)和轮廓他们决定什么是最适合你的特殊用途的情况下 ,你在推销自己的短。
两种选择对你的情况完全不恰当的。
TaskCreationOptions.LongRunning
肯定是不属于CPU密集型任务,如在TPL(一个更好的选择Parallel
类/扩展)几乎完全意味着通过在多个内核(不是线程)运行它最大化CPU绑定操作的吞吐量。
然而,1000组的任务是为这个不可接受的数字。 不管是不是他们都同时运行是不完全的问题; 甚至100个线程等待同步I / O是一个无法维持的局面。 作为一个评论指出,您的应用程序将使用的内存的大量和最终花费几乎所有的时间都处于上下文切换。 该TPL不适合这个规模。
如果你的操作是I / O限制-如果你正在使用的网络服务, 他们是 -然后异步I / O不仅是正确的解决方案,这是唯一的解决办法。 如果必须重新设计你的一些代码(如,例如,增加异步方法来那里有没有最初主要接口), 这样做 ,因为I / O完成端口是在Windows或.NET的唯一机制,可以很好地支持这种特殊类型的并发性。
我从来没有听说过的情况下异步I / O在某种程度上“不是一种选择”。 我甚至不能想象任何有效的情况下,使用此约束。 如果您无法异步我用/ O那么这将表明一个严重的设计问题,即必须是固定的, 尽快 。
虽然这不是一个直接的比较,我认为它可以帮助你。 我做类似你的描述(在我的情况下,我知道有关于REST服务电话另一端的负载平衡服务器集群)的东西。 我取得好成绩使用Parrallel.ForEach
旋转起来只要我还使用了下面的代码 ,告诉我的操作系统它可以连接到比平常的端点多个工作线程的最佳数目。
var servicePointManager = System.Net.ServicePointManager.FindServicePoint(Uri);
servicePointManager.ConnectionLimit = 250;
请注意,您必须调用一次为你连接到每一个独特的URL。
文章来源: Task.Factory.StartNew or Parallel.ForEach for many long-running tasks? [duplicate]