ParallelOptions.MaxDegreeOfParallelism 不是全局的,它是每个并行循环的。更具体地说,它设置可以并行运行的最大任务数,而不是并行运行这些任务的最大内核或线程数。
一些演示测试
注意:我有 4 个内核,8 个线程
代码中发生了什么
- 我们正在运行 2 个异步方法;每一个都启动嵌套的并行循环。
- 我们将最大并行度设置为 2,睡眠时间设置为 2 秒,以模拟每个任务所做的工作
- 因此,由于将
MaxDegreeOfParallelism 设置为 2,我们预计在 40 个任务完成之前达到 12 个并发任务(我只计算嵌套并行循环启动的任务)
- 如何获得 12?
- 在外循环中启动了最多 2 个并发任务
- +4 最大并发任务来自内循环(每个任务在外循环中启动 2 个)
- 这是 6(每个异步任务在
Main 开始)
- 共 12 个
测试代码
using System;
using System.Threading;
using System.Threading.Tasks;
namespace forfun
{
class Program
{
static void Main(string[] args)
{
var taskRunner = new TaskRunner();
taskRunner.RunTheseTasks();
taskRunner.RunTheseTasksToo();
Console.ReadLine();
}
private class TaskRunner
{
private int _totalTasks = 0;
private int _runningTasks = 0;
public async void RunTheseTasks()
{
await Task.Run(() => ProcessThingsInParallel());
}
public async void RunTheseTasksToo()
{
await Task.Run(() => ProcessThingsInParallel());
}
private void ProcessThingsInParallel()
{
ParallelOptions po = new ParallelOptions();
po.MaxDegreeOfParallelism = 2;
Parallel.For(0, 4, po, (i) =>
{
Interlocked.Increment(ref _totalTasks);
Interlocked.Increment(ref _runningTasks);
Console.WriteLine($"{_runningTasks} currently running of {_totalTasks} total tasks");
Parallel.For(0, 4, po, (j) =>
{
Interlocked.Increment(ref _totalTasks);
Interlocked.Increment(ref _runningTasks);
Console.WriteLine($"{_runningTasks} currently running of {_totalTasks} total tasks");
WorkMethod(i, j); // assume a long-running method
Interlocked.Decrement(ref _runningTasks);
});
Interlocked.Decrement(ref _runningTasks);
}
);
}
private static void WorkMethod(int i, int l)
{
Thread.Sleep(2000);
}
}
}
}
剧透,输出显示设置MaxDegreeOfParallelism不是全局的,不限于核心或线程数,而是专门设置并发运行任务的最大值。
最大值设置为 2 的输出:
1 currently running of 1 total tasks
3 currently running of 3 total tasks
2 currently running of 2 total tasks
4 currently running of 4 total tasks
5 currently running of 5 total tasks
7 currently running of 7 total tasks
[ ... snip ...]
11 currently running of 33 total tasks
12 currently running of 34 total tasks
11 currently running of 35 total tasks
12 currently running of 36 total tasks
11 currently running of 37 total tasks
12 currently running of 38 total tasks
11 currently running of 39 total tasks
12 currently running of 40 total tasks
(输出会有所不同,但每次最大并发数应该是12)
没有设置最大值的输出:
1 currently running of 1 total tasks
3 currently running of 3 total tasks
4 currently running of 4 total tasks
2 currently running of 2 total tasks
5 currently running of 5 total tasks
7 currently running of 7 total tasks
[ ... snip ...]
19 currently running of 28 total tasks
19 currently running of 29 total tasks
18 currently running of 30 total tasks
13 currently running of 31 total tasks
13 currently running of 32 total tasks
16 currently running of 35 total tasks
16 currently running of 36 total tasks
14 currently running of 33 total tasks
15 currently running of 34 total tasks
15 currently running of 37 total tasks
16 currently running of 38 total tasks
16 currently running of 39 total tasks
17 currently running of 40 total tasks
请注意,如果不设置最大值,我们会获得多达 19 个并发任务
- 现在 2 秒的睡眠时间限制了可以在其他任务完成之前启动的任务数量
睡眠时间增加到 12 秒后输出
1 currently running of 1 total tasks
2 currently running of 2 total tasks
3 currently running of 3 total tasks
4 currently running of 4 total tasks
[ ... snip ...]
26 currently running of 34 total tasks
26 currently running of 35 total tasks
27 currently running of 36 total tasks
28 currently running of 37 total tasks
28 currently running of 38 total tasks
28 currently running of 39 total tasks
28 currently running of 40 total tasks
最多有 28 个并发任务
现在将循环设置为 10 嵌套在 10 中,并将睡眠时间设置回 2 秒 - 再次没有设置最大值
1 currently running of 1 total tasks
3 currently running of 3 total tasks
2 currently running of 2 total tasks
4 currently running of 4 total tasks
[ ... snip ...]
38 currently running of 176 total tasks
38 currently running of 177 total tasks
38 currently running of 178 total tasks
37 currently running of 179 total tasks
38 currently running of 180 total tasks
38 currently running of 181 total tasks
[ ... snip ...]
35 currently running of 216 total tasks
35 currently running of 217 total tasks
32 currently running of 218 total tasks
32 currently running of 219 total tasks
33 currently running of 220 total tasks
在所有 220 个任务完成之前获得多达 38 个并发任务
更多相关信息
ParallelOptions.MaxDegreeOfParallelism Property
MaxDegreeOfParallelism 属性会影响通过传递此 ParallelOptions 实例的 Parallel 方法调用运行的并发操作数。正属性值将并发操作数限制为设置值。如果为-1,则对并发运行的操作数没有限制。
默认情况下,无论底层调度程序提供多少线程,For 和 ForEach 都会使用,因此从默认值更改 MaxDegreeOfParallelism 只会限制将使用多少并发任务。
要获得最大并行度,不要设置它,而是让 TPL 及其调度程序处理它
设置最大并行度只影响并发任务的数量,不影响使用的线程数
最大并发任务数不等于可用线程数——线程仍然可以处理多个任务;即使您的应用程序正在使用所有线程,它仍然会与机器托管的其他进程共享这些线程
Environment.ProcessorCount
获取当前机器上的处理器数量。
如果我们说MaxDegreeOfParallelism = Environment.ProcessorCount呢?
即使将最大并行度设置为Environment.ProcessorCount 也无法动态确保无论您的应用在哪个系统上运行都能获得最大并发。这样做仍然会限制并行度,因为任何给定的线程都可以在许多任务之间切换——所以这只会将并发任务的数量限制为等于可用线程的数量——这并不一定意味着每个并发任务都会以一对一的关系整齐地分配给每个线程。