簡體   English   中英

Parallel operations in .NET 6中的MaxDegreeOfParallelism = -1是什么意思?

[英]What is the meaning of the MaxDegreeOfParallelism = -1 in Parallel operations in .NET 6?

ParallelOptions.MaxDegreeOfParallelism屬性的文檔指出:

MaxDegreeOfParallelism屬性影響通過此ParallelOptions實例傳遞的Parallel方法調用運行的並發操作數。 正的屬性值將並發操作數限制為設置值。 如果為-1,則表示並發運行的操作數沒有限制。

默認情況下, ForForEach將使用底層調度程序提供的任意多個線程,因此從默認值更改MaxDegreeOfParallelism僅限制將使用的並發任務數。

我試圖理解“無限制”在這種情況下的含義。 根據上述文檔摘錄,我的期望是配置有MaxDegreeOfParallelism = -1Parallel.Invoke操作將立即開始並行執行所有提供的actions 但這不是正在發生的事情。 這是一個包含 12 個動作的實驗:

int concurrency = 0;
Action action = new Action(() =>
{
    var current = Interlocked.Increment(ref concurrency);
    Console.WriteLine(@$"Started an action at {DateTime
        .Now:HH:mm:ss.fff} on thread #{Thread
        .CurrentThread.ManagedThreadId} with concurrency {current}");
    Thread.Sleep(1000);
    Interlocked.Decrement(ref concurrency);
});
Action[] actions = Enumerable.Repeat(action, 12).ToArray();
var options = new ParallelOptions() { MaxDegreeOfParallelism = -1 };
Parallel.Invoke(options, actions);

Output:

Started an action at 11:04:42.636 on thread #6 with concurrency 4
Started an action at 11:04:42.636 on thread #7 with concurrency 5
Started an action at 11:04:42.629 on thread #1 with concurrency 1
Started an action at 11:04:42.636 on thread #8 with concurrency 3
Started an action at 11:04:42.630 on thread #4 with concurrency 2
Started an action at 11:04:43.629 on thread #9 with concurrency 6
Started an action at 11:04:43.648 on thread #6 with concurrency 6
Started an action at 11:04:43.648 on thread #8 with concurrency 6
Started an action at 11:04:43.648 on thread #4 with concurrency 6
Started an action at 11:04:43.648 on thread #7 with concurrency 6
Started an action at 11:04:43.648 on thread #1 with concurrency 6
Started an action at 11:04:44.629 on thread #9 with concurrency 6

現場演示

這個實驗的結果與我的預期不符。 並非所有操作都被立即調用。 記錄的最大並發是6,有時是7,但不是12。所以“不限制”並不是我想的意思。 我的問題是:對於所有四種Parallel方法( ForForEachForEachAsyncInvoke ), MaxDegreeOfParallelism = -1配置究竟意味着什么? 我想詳細了解以這種方式配置時這些方法的行為是什么。 如果 .NET 版本之間存在行為差異,我對當前的 .NET 版本 (.NET 6) 感興趣,該版本還引入了新的Parallel.ForEachAsync API。

第二個問題: MaxDegreeOfParallelism = -1是否與在這些方法中省略可選的parallelOptions參數完全相同?


說明我對使用默認TaskScheduler 配置Parallel方法的行為感興趣。 我對使用專門或自定義調度程序可能出現的任何復雜情況感興趣。

定義中特意聲明為-1 means that the number of number of concurrent operations will not be artificially limited. 它並沒有說所有的動作都會立即開始。

線程池管理器通常將可用線程數保持在內核數(或邏輯處理器是內核數的 2 倍),這被認為是最佳線程數(我認為這個數字是 [內核數/邏輯處理器 + 1]) 這意味着當您開始執行您的操作時,立即開始工作的可用線程數就是這個數字。

線程池管理器定期運行(每秒兩次),如果沒有線程完成,則添加一個新線程(或者在線程太多時在相反的情況下刪除)。

一個很好的實驗是快速連續運行兩次實驗,以了解這一點。 在第一個實例中,開始時並發作業的數量應該大約是內核/邏輯處理器的數量 + 1,而在第二次運行中,它應該是運行的作業數量(因為創建這些線程是為了服務第一次運行):

這是您的代碼的修改版本:

using System.Diagnostics;

Stopwatch sw = Stopwatch.StartNew();
int concurrency = 0;
Action action = new Action(() =>
{
    var current = Interlocked.Increment(ref concurrency);
    Console.WriteLine(@$"Started at {sw.ElapsedMilliseconds} with concurrency {current}");
    Thread.Sleep(10_000);
    current = Interlocked.Decrement(ref concurrency);
});


Action[] actions = Enumerable.Repeat(action, 12).ToArray();
var options = new ParallelOptions() { MaxDegreeOfParallelism = -1 };
Parallel.Invoke(options, actions);

Parallel.Invoke(options, actions);

Output:

Started at 114 with concurrency 8
Started at 114 with concurrency 1
Started at 114 with concurrency 2
Started at 114 with concurrency 3
Started at 114 with concurrency 4
Started at 114 with concurrency 6
Started at 114 with concurrency 5
Started at 114 with concurrency 7
Started at 114 with concurrency 9
Started at 1100 with concurrency 10
Started at 2097 with concurrency 11
Started at 3100 with concurrency 12
Started at 13110 with concurrency 1
Started at 13110 with concurrency 2
Started at 13110 with concurrency 3
Started at 13110 with concurrency 5
Started at 13110 with concurrency 7
Started at 13110 with concurrency 9
Started at 13110 with concurrency 10
Started at 13110 with concurrency 11
Started at 13110 with concurrency 4
Started at 13110 with concurrency 12
Started at 13110 with concurrency 6
Started at 13110 with concurrency 8

我的計算機有 4 個內核(8 個邏輯處理器),當作業在“冷” TaskScheduler.Default上運行時。默認首先立即啟動其中的 8+1 個,然后定期添加一個新線程。

然后,當運行第二批“熱”時,所有作業同時開始。

並行.ForEachAsync

當使用Parallel.ForEachAsync運行類似示例時,行為會有所不同。 這項工作是在恆定的並行水平下完成的。 請注意,這與線程無關,因為如果您await Task.Delay (因此不阻塞線程),並行作業的數量將保持不變。

如果我們查看采用ParallelOptions的版本的源代碼,它會將parallelOptions.EffectiveMaxConcurrencyLevel作為dop傳遞給執行實際工作的私有方法。

public static Task ForEachAsync<TSource>(IEnumerable<TSource> source!!, ParallelOptions parallelOptions!!, Func<TSource, CancellationToken, ValueTask> body!!)
{
     return ForEachAsync(source, parallelOptions.EffectiveMaxConcurrencyLevel, ...);
}

如果我們進一步觀察,我們可以看到:

  • “dop”記錄為 ' A integer 表示允許並行運行多少個操作。 '.
  • 實際的並行級別是DefaultDegreeOfParallelism
/// <param name="dop">A integer indicating how many operations to allow to run in parallel.</param>
(...)
private static Task ForEachAsync<TSource>(IEnumerable<TSource> source, int dop,
{
    ...

    if (dop < 0)
    {
        dop = DefaultDegreeOfParallelism;
    }

最后一瞥,我們可以看到最終值為Environment.ProcessorCount

private static int DefaultDegreeOfParallelism => Environment.ProcessorCount;

這就是現在的樣子,我不確定在 .NET 7 中是否會保持這樣。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM