Parallel.ForEach仅当任务受CPU限制并且线性扩展时, 默认选项 才可以正常工作
。当任务受CPU限制时,一切都将正常运行。如果您具有四核并且没有其他进程在运行,则
Parallel.ForEach使用所有四个处理器。如果您具有四核,并且计算机上的某些其他进程正在使用一个完整的CPU,则
Parallel.ForEach大约使用三个处理器。
但是,如果任务不受CPU限制,则
Parallel.ForEach继续启动任务,努力使所有CPU保持忙碌状态。但是,无论并行执行多少任务,总是有更多未使用的CPU功能,因此它会继续创建任务。
如何判断您的任务是否受CPU限制?希望只是通过检查。如果要分解素数,这是显而易见的。但是其他情况并不那么明显。判断任务是否受CPU限制的经验方法是限制最大并行度,
ParallelOptions.MaximumDegreeOfParallelism并观察程序的行为。如果您的任务是CPU密集型的,那么您应该在四核系统上看到这样的模式:
ParallelOptions.MaximumDegreeOfParallelism = 1
:使用一个完整的CPU或25%的CPU利用率ParallelOptions.MaximumDegreeOfParallelism = 2
:使用两个CPU或50%的CPU利用率ParallelOptions.MaximumDegreeOfParallelism = 4
:使用所有CPU或100%CPU利用率
如果行为如此,则可以使用默认
Parallel.ForEach选项并获得良好的效果。线性的CPU利用率意味着良好的任务调度。
但是,如果我在Intel
i7上运行示例应用程序,则无论我设置的最大并行度如何,我都会获得约20%的CPU利用率。为什么是这样?由于分配了太多内存,垃圾回收器阻塞了线程。应用程序是资源绑定的,资源是内存。
同样,对数据库服务器执行长时间运行查询的I /
O绑定任务也永远无法有效利用本地计算机上可用的所有CPU资源。并且在这种情况下,任务计划程序无法“知道何时停止”开始新任务。
如果您的任务不受CPU限制,或者CPU利用率不能以最大并行度线性扩展,则应建议
Parallel.ForEach不要一次启动太多任务。最简单的方法是指定一个数字,该数字允许对重叠的I
/ O绑定任务进行某种并行处理,但又不能过多,以至于使本地计算机对资源的需求不堪重负,或者使任何远程服务器负担过多。反复试验才能获得最佳结果:
static void Main(string[] args){ Parallel.ForEach(CreateData(), new ParallelOptions { MaxDegreeOfParallelism = 4 }, (data) => { data[0] = 1; });}


