PLINQ (Parallel LINQ)

データを分割し、複数のプロセッサでクエリを並列実行 (execute in parallel) させられます。

ParallelEnumerableクラス

メソッド 機能
AsParallel(IEnumerable) クエリの並列化 (parallelization) を有効にする
AsSequential<TSource>(ParallelQuery<TSource>) ParallelQuery<TSource>をIEnumerable<T>へ変換し、クエリを順に評価することを強制する
AsOrdered(ParallelQuery) データソースの処理を、順序ありとできる
WithExecutionMode<TSource>(ParallelQuery<TSource>, ParallelExecutionMode) クエリの実行モードを設定できる
   
ParallelEnumerable クラス (System.Linq) | Microsoft Learn

WithExecutionMode<TSource>()

クエリの実行モードを設定できます。そのとき並列に実行することを強制することで、クエリの構造と無関係に並列化できます。

public static System.Linq.ParallelQuery<TSource> WithExecutionMode<TSource> (
    this System.Linq.ParallelQuery<TSource> source,
    System.Linq.ParallelExecutionMode executionMode
    );
ParallelEnumerable.WithExecutionMode<TSource> メソッド (System.Linq) | Microsoft Learn
IEnumerable<int> source = Enumerable.Range(0, 10);
Func<int, bool> selector = (num) =>
{
    Task.Delay(100).Wait();
    return num % 2 == 0;
};


IEnumerable<int> query1 = // LINQ
    from num in source
    where selector(num)
    select num;

ParallelQuery<int> query2 = // PLINQ
    from num in source.AsParallel()
    where selector(num)
    select num;

ParallelQuery<int> query3 = // PLINQ 並列を強制
    from num in source.AsParallel().WithExecutionMode(ParallelExecutionMode.ForceParallelism)
    where selector(num)
    select num;

int r1 = query1.Count(); // 約1100 ms
int r2 = query2.Count(); // 約1100 ms
int r3 = query3.Count(); // 約220 ms

int r4 = query1.Count(); // 約1100 ms
int r5 = query2.Count(); // 約220 ms

AsOrdered<TSource>()

PLINQでは並列に実行されることで、既定では順序が維持されません。これを維持するように指示できます。

public static System.Linq.ParallelQuery<TSource> AsOrdered<TSource> (
    this System.Linq.ParallelQuery<TSource> source
    );
AsOrdered<TSource>(ParallelQuery<TSource>) - ParallelEnumerable.AsOrdered メソッド (System.Linq) | Microsoft Learn

順序の維持が不要となったら、AsUnordered<TSource>()を呼び出しそれを無効にします。

orderbyなどで並べ替えると、AsUnordered<TSource>()が呼び出されるまで順序が維持されます。Query Operators and Ordering - Order Preservation in PLINQ - .NET | Microsoft Learn

IEnumerable<int> source = Enumerable.Range(0, 10);

Func<int, bool> selector = (num) =>
{
    Task.Delay(1).Wait();
    return num % 2 == 0;
};


IEnumerable<int> query1 = // LINQ
    from num in source
    where selector(num)
    select num;

ParallelQuery<int> query2 = // PLINQ
    from num in source.AsParallel()
    where selector(num)
    select num;

ParallelQuery<int> query3 = // PLINQ AsOrdered()指定
    from num in source.AsParallel().AsOrdered()
    where selector(num)
    select num;

ParallelQuery<int> query4 = // PLINQ orderbyで並べ替え
    from num in source.AsParallel()
    where selector(num)
    orderby num
    select num;

int[] a1 = query1.ToArray(); // 0, 2, 4, 6, 8
int[] a2 = query2.ToArray(); // 0, 8, 2, 4, 6
int[] a3 = query3.ToArray(); // 0, 2, 4, 6, 8
int[] a4 = query4.ToArray(); // 0, 2, 4, 6, 8
Microsoft Learnから検索