2013-02-04 7 views
5

linqに関する素晴らしい点の1つは、リクエストに応じて遅延して処理される無限のデータソースを持つことでした。クエリを並列化しようとしましたが、遅延ロードが機能していないことがわかりました。たとえば...PLINQで遅延ロードを取得するにはどうすればよいですか?

class Program 
{ 
    static void Main(string[] args) 
    { 
     var source = Generator(); 
     var next = source.AsParallel().Select(i => ExpensiveCall(i)); 
     foreach (var i in next) 
     { 
      System.Console.WriteLine(i); 
     } 
    } 

    public static IEnumerable<int> Generator() 
    { 
     int i = 0; 
     while (true) 
     { 
      yield return i; 
      i++; 
     } 
    } 

    public static int ExpensiveCall(int arg) 
    { 
     System.Threading.Thread.Sleep(5000); 
     return arg*arg; 
    } 
} 

このプログラムは、おそらく各ステップで、もちろん決して枯渇する発電機へのすべての呼び出しのための待ち時間を、任意の結果を生成するために失敗しました。私が "AsParallel"呼び出しを取り出すと、正常に動作します。では、PLINQを使用してアプリケーションのパフォーマンスを向上させながら、どうやって怠惰なローディングを得るのですか?

答えて

5

は、私は次の2つの異なるものを混乱していると思いますMergeOptions

var next = source.AsParallel() 
       .WithMergeOptions(ParallelMergeOptions.NotBuffered) 
       .Select(i => ExpensiveCall(i)); 
2

を見てみましょう。ここでの問題はレイジーローディング(すなわち、必要なだけのローディング)ではなく、出力バッファリング(すなわち、すぐに結果を返さない)である。

あなたのケースでは、最終的に結果が得られますが、(私の場合、最初のバッチを返すためには500の結果が必要です)。バッファリングはパフォーマンス上の理由から行われますが、あなたの場合はそれは意味をなさない。イアンが正しく指摘したように、出力バッファリングを無効にするには、.WithMergeOptions(ParallelMergeOptions.NotBuffered)を使用する必要があります。

私が知る限り、PLINQは遅延読み込みを行わず、変更する方法はありません。つまり、コンシューマ(あなたのケースではforeachループ)が遅すぎると、PLINQは結果を必要以上に速く生成し、結果の反復処理を終了したときにのみ停止します。これは、PLINQがCPU時間とメモリを無駄にする可能性があることを意味します。

+0

非常に良い点... PLINQでのバッファリングは、遅延ロードがないという問題のみをマスクします。たぶん行く方法は、次のn個のアイテムをバッチ処理し、それらを並列に実行して結果を返す拡張メソッドを持つことです。それは擬似怠惰な行動を生み出すことができます... – tbischel

+0

@tbischelうん、そういうものがうまくいくでしょう。もう一つのオプションは 'BoundedCapacity'がセットされた' BlockingCollection'を使うことです。 – svick

関連する問題