12

PLINQでの注文の保存に関するmsdnのドキュメントには、について次のように記載されていますForAll()

  • ソースシーケンスが順序付けられたときの結果:非決定論的に並行して実行されます
  • ソースシーケンスが順序付けされていない場合の結果:非決定論的に並行して実行されます

これは、メソッドの順序付けられた実行がForAll保証されないことを意味しますか?

私はこれまでPLINQを使用したことがありませんが、次のコードレビューの質問は適切な使用法のように思われました。私の答えの一番下に私は書いています:

Events.AsParallel().AsOrdered().ForAll( eventItem =>
{
    ...
} );    

ドキュメントを読んだ後、私はAsOrdered()何も変わらないと思いますか?
また、前のクエリforでは、順序が重要な単純なループを置き換えることができないのではないかと思います。
おそらく、への並列呼び出しStringBuilderも発生し、間違った出力になりますか?

4

6 に答える 6

16

順序の保存は通常、結果にのみ適用されます。つまり、入力は任意の順序で処理できますが、元の順序で返されます。

ForAll何も返さないので、私が知っている効果は実際にはありません。

順序付けを処理に適用する唯一の方法は、アイテム1を処理する前にアイテム0を終了し、アイテム2を処理する前などです。この時点では、並列処理はありません。

于 2011-03-18T13:23:42.667 に答える
8

他の人が正しく答えているように、ForAllメソッドは特定の順序で列挙可能な要素に対してアクションを実行することが保証されることはなく、AsOrdered()メソッド呼び出しを黙って無視します。

元の順序にできるだけ近い方法で列挙可能な要素に対してアクションを実行する正当な理由がある読者の利益のために(並列処理のコンテキストで合理的である限り)、以下の拡張メソッドが役立つ場合があります。

public static void ForAllInApproximateOrder<TSource>(this ParallelQuery<TSource> source, Action<TSource> action) {

    Partitioner.Create( source )
               .AsParallel()
               .AsOrdered()
               .ForAll( e => action( e ) );

}

これは、次のように使用できます。

orderedElements.AsParallel()
               .ForAllInApproximateOrder( e => DoSomething( e ) );

上記の拡張メソッドはPLINQForAllを使用するのではなくParallel.ForEach、PLINQによって内部的に使用されるスレッドモデルを継承することに注意してください(これはParallel.ForEach、私の経験ではデフォルトではそれほど積極的ではありません)。を使用した同様の拡張方法をParallel.ForEach以下に示します。

public static void ForEachInApproximateOrder<TSource>(this ParallelQuery<TSource> source, Action<TSource> action) {

    source = Partitioner.Create( source )
                        .AsParallel()
                        .AsOrdered();

    Parallel.ForEach( source , e => action( e ) );

}

これは、次のように使用できます。

orderedElements.AsParallel()
               .ForEachInApproximateOrder( e => DoSomething( e ) );

上記の拡張メソッドのいずれかを使用する場合、クエリにチェーンAsOrdered()する必要はありません。とにかく内部的に呼び出されます。

私はこれらの方法が粗い粒子の重要性を持つ要素を処理するのに役立つことを発見しました。たとえば、最も古いものから始めて、最も新しいものに向かって作業するレコードを処理すると便利な場合があります。多くの場合、レコードの正確な順序は必要ありません。通常、古いレコードが新しいレコードの前に処理される限りです。同様に、優先度が低い/中程度/高いレコードは、ほとんどの場合、優先度の高いレコードが優先度の低いレコードの前に処理されるように処理できますが、エッジケースはそれほど遅れていません。

于 2014-01-05T01:42:46.450 に答える
6

AsOrdered()何も変更されません-並列クエリの結果に順序を適用する場合は、並列処理を利用するために使用できます。つまりforeach() ForAll()、コレクション内の複数のアイテムに対して一度に副作用を実行することを意味します。実際、順序付けはクエリの結果(結果コレクション内のアイテムの順序)にのみ適用されますが、順序にはまったく影響しないため、これはとは関係ありません。ForAll()ForAll()

PLINQの目標は、正確性を維持しながらパフォーマンスを最大化することです。クエリは可能な限り高速に実行する必要がありますが、それでも正しい結果が得られます。場合によっては、正確さのためにソースシーケンスの順序を保持する必要があります

ForAll()コレクションを変換するのではなく(つまり、新しいコレクションに投影するのではない)、PLINQクエリの結果に副作用を実行するためだけのものであることに注意してください。

于 2011-03-18T13:22:54.933 に答える
4

これは、ForAllメソッドの順序付けられた実行が保証されないことを意味しますか?

はい-注文は保証されません。

並列化とは、作業が異なるスレッドに割り当てられ、それらの個別の出力が後で結合されることを意味します。

出力を注文する必要がある場合は、PLinqを使用しないでください。または、注文を元に戻すために後の手順を追加してください。


また、plinq実行内からStringBuilderなどのオブジェクトにアクセスする場合は、それらのオブジェクトがスレッドセーフであることを確認してください。また、このスレッドセーフにより、実際にはplinqが非並列linqよりも遅くなる可能性があることに注意してください。

于 2011-03-18T13:22:18.477 に答える
2

拡張メソッドとして:

複数のコアで処理されてから結果が順序付けられるため、順序付けのオーバーヘッドが発生します。これは、vsparallelの単純なベンチマークに関する回答です。

 public static IEnumerable<T1> OrderedParallel<T, T1>(this IEnumerable<T> list, Func<T, T1> action)
    {
        var unorderedResult = new ConcurrentBag<(long, T1)>();
        Parallel.ForEach(list, (o, state, i) =>
        {
            unorderedResult.Add((i, action.Invoke(o)));
        });
        var ordered = unorderedResult.OrderBy(o => o.Item1);
        return ordered.Select(o => o.Item2);
    }

次のように使用します:

var result = Events.OrderedParallel(eventItem => ...);

これで時間を節約できることを願っています。

于 2020-03-14T10:45:26.083 に答える
-1

ForAll、アクションを複数のスレッドで並行して実行します。いつでも複数のアクションが同時に実行され、これらの状況では「順序」の概念は適用されません。アクションを順番に実行するには、アクションを順番に実行する必要があります。最も簡単な方法は、アクションを単一のスレッドで実行することです。これは、クエリの結果を標準foreachループで列挙するだけで実現できます。

var query = Events.AsParallel().AsOrdered();
foreach (var eventItem in query)
{
    // do something with the eventItem
}

流暢な構文が必要な場合は、以下の拡張メソッドForAllを使用してプロジェクトに静的クラスを追加できます。ForEach

public static void ForEach<TSource>(this IEnumerable<TSource> source,
    Action<TSource> action)
{
    foreach (TSource item in source)
    {
        action(item);
    }
}

そして、次のように使用します。

Events.AsParallel().AsOrdered().ForEach(eventItem =>
{
    // do something with the eventItem
});

ただし、指定された例では、ParallelLINQの使用は冗長であることに注意してください。クエリEvents.AsParallel().AsOrdered()は列挙可能なソースへの変換を実行しないため、実際の計算は行われません。パーツを削除し.AsParallel().AsOrdered()て同じ結果を得ることができます。

于 2020-03-14T12:29:35.027 に答える