Aka*_*ava 49 c# .net-4.0 parallel-extensions task-parallel-library parallel.foreach
我正在使用Parallel.ForEach并且我正在做一些数据库更新,现在没有设置MaxDegreeOfParallelism,双核处理器机器导致sql客户端超时,否则四核处理器机器不知何故不会超时.
现在我无法控制我的代码运行的哪种处理器内核可用,但是我可以使用MaxDegreeOfParallelism更改某些设置,这些设置可能会同时运行较少的操作而不会导致超时?
我可以增加超时但它不是一个好的解决方案,如果在较低的CPU上我可以同时处理较少的操作,这将减少对CPU的负载.
好的我也读过所有其他帖子和MSDN,但是将MaxDegreeOfParallelism设置为较低值会让我的四核机器遭受损失吗?
例如,无论如何,如果CPU有两个内核,那么使用20,如果CPU有四个内核,那么40?
小智 67
答案是它是整个并行操作的上限,与核心数无关.
因此,即使您因为等待IO或锁定而不使用CPU,也不会并行运行额外的任务,只有您指定的最大值.
为了找到这个,我写了这段测试代码.那里有一个人工锁,可以刺激TPL使用更多的线程.当您的代码等待IO或数据库时,也会发生同样的情况.
class Program
{
static void Main(string[] args)
{
var locker = new Object();
int count = 0;
Parallel.For
(0
, 1000
, new ParallelOptions { MaxDegreeOfParallelism = 2 }
, (i) =>
{
Interlocked.Increment(ref count);
lock (locker)
{
Console.WriteLine("Number of active threads:" + count);
Thread.Sleep(10);
}
Interlocked.Decrement(ref count);
}
);
}
}
Run Code Online (Sandbox Code Playgroud)
如果我没有指定MaxDegreeOfParallelism,则控制台日志记录显示最多同时运行大约8个任务.像这样:
Number of active threads:6
Number of active threads:7
Number of active threads:7
Number of active threads:7
Number of active threads:7
Number of active threads:7
Number of active threads:6
Number of active threads:7
Number of active threads:7
Number of active threads:7
Number of active threads:7
Number of active threads:7
Number of active threads:7
Number of active threads:7
Number of active threads:7
Number of active threads:7
Number of active threads:7
Number of active threads:7
Number of active threads:7
Run Code Online (Sandbox Code Playgroud)
它开始降低,随着时间的推移而增加,最后它试图同时运行8.
如果我将它限制在某个任意值(比如2),我会得到
Number of active threads:2
Number of active threads:1
Number of active threads:2
Number of active threads:2
Number of active threads:2
Number of active threads:2
Number of active threads:2
Number of active threads:2
Number of active threads:2
Number of active threads:2
Number of active threads:2
Number of active threads:2
Number of active threads:2
Number of active threads:2
Number of active threads:2
Number of active threads:2
Number of active threads:2
Run Code Online (Sandbox Code Playgroud)
哦,这是在四核机器上.
bug*_*d87 17
例如,无论如何,如果CPU有两个内核,那么使用20,如果CPU有四个内核,那么40?
您可以执行此操作以使并行性取决于CPU核心数:
var options = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount * 10 };
Parallel.ForEach(sourceCollection, options, sourceItem =>
{
// do something
});
Run Code Online (Sandbox Code Playgroud)
但是,较新的CPU倾向于使用超线程来模拟额外的内核.因此,如果你有一个四核处理器,那么Environment.ProcessorCount
可能会将其报告为8核.我发现如果你将并行性设置为模拟核心,那么它实际上会减慢其他线程,如UI线程.
因此,尽管操作完成得更快,但应用程序UI在此期间可能会遇到明显的延迟.将"Environment.ProcessorCount"除以2似乎可以实现相同的处理速度,同时仍然保持CPU可用于UI线程.
该Parallel.ForEach
方法在内部启动多个Task
任务,每个任务都会重复从source
序列中获取一个项目并调用body
该项目的委托。可以MaxDegreeOfParallelism
设置这些内部任务的上限。但这个设置并不是限制并行性的唯一因素。还愿意TaskScheduler
执行由 产生的任务Parallel.ForEach
。
生成机制的工作原理是每个生成的任务都进行自我复制。换句话说,每个任务要做的第一件事就是创建另一个任务。大多数TaskScheduler
s 对可以同时执行的任务数量有限制,当达到此限制时,它们会对下一个传入的任务进行排队,而不是立即执行它们。因此,最终的自我复制模式Parallel.ForEach
将停止生成更多任务,因为最后生成的任务将闲置在队列中TaskScheduler
。
我们来谈谈TaskScheduler.Default
,它是 的默认调度程序Parallel.ForEach
,并在 上调度任务ThreadPool
。有ThreadPool
软限制和硬限制。软限制是指工作需求不能立即得到满足,而硬限制是指工作需求在已运行的工作项完成之前永远不会得到满足。当ThreadPool
达到默认的软限制时Environment.ProcessorCount
,它会以每秒一个新线程\xc2\xb9 的频率生成更多线程以满足需求。可以通过该方法配置软限制ThreadPool.SetMinThreads
。可以通过方法找到硬限制ThreadPool.GetMaxThreads
,在我的机器上是 32,767 个线程。
Parallel.ForEach
因此,如果我在我的 4 核机器中配置MaxDegreeOfParallelism = 20
,并且body
委托使当前线程忙碌超过一秒,则有效并行度将从 5 开始,然后在接下来的 15 秒内逐渐增加,直到变成20,并且它将保持在 20 直到循环完成。它以 5 而不是 4 开头的原因是因为它还Parallel.ForEach
使用当前线程以及ThreadPool
.
如果我不配置MaxDegreeOfParallelism
,则与配置 value 相同-1
,这意味着无限的并行性。在这种情况下,ThreadPool
可用性将是实际并行度的唯一限制因素。只要Parallel.ForEach
运行,就会ThreadPool
饱和,也就是说,就会处于供给不断超过需求的情况。每次 生成新线程时ThreadPool
,该线程都会选择 之前安排的最后一个任务Parallel.ForEach
,该任务将立即复制自身,并且副本将进入 的ThreadPool
队列。如果该Parallel.ForEach
意志运行足够长的时间,该意志ThreadPool
将达到其最大大小(在我的机器中为32,767),并将保持在这个水平直到循环完成。这是假设进程不会因为缺乏内存等其他资源而崩溃。
该属性的官方文档MaxDegreeOfParallelism
指出“一般情况下,您不需要修改此设置”。显然,自从 .NET Framework 4.0 (2010) 引入 TPL 以来,情况一直如此。此时您可能已经开始质疑此建议的有效性。我也是,所以我在 dotnet/runtime 存储库上发布了一个问题,询问给定的建议是否仍然有效或者已经过时。我很惊讶地收到反馈说这些建议一如既往地有效。微软的说法是,将 限制为该MaxDegreeOfParallelism
值Environment.ProcessorCount
可能会导致性能下降,甚至在某些场景下出现死锁。我用几个例子来回应,展示了当未配置的应用程序Parallel.ForEach
在启用异步的应用程序中运行时可能出现的问题行为,其中其他事情与并行循环同时发生。这些演示被认为不具有代表性,因为我使用了Thread.Sleep
模拟循环内部工作的方法。
我个人的建议是:无论何时使用任何Parallel
方法,都始终指定MaxDegreeOfParallelism
,即使您对默认值感到满意。如果您相信我的论点,即饱和ThreadPool
是不可取的,您可以将其配置为合适的值,例如Environment.ProcessorCount
。如果您相信微软关于性能和死锁的论点,您可以使用-1
(默认值)进行配置。无论哪种情况,任何看到您的代码的人都会被暗示您做出了一个有意识且明智的决定。
\xc2\xb9的注入速率ThreadPool
没有记录。“每秒一个新线程”是一项实验观察。
归档时间: |
|
查看次数: |
50051 次 |
最近记录: |