2016-02-12 20 views
5

私は大きなファイルを1行ずつ読み込み、いくつかのロジックを行い、ファイルに書き込む必要のある文字列を返すタスクを持っています。出力の順序は関係ありません。しかし、以下のコードを試してみると、私のファイルの15〜20k行を読み終えた後は、実際には停止/停止が遅くなります。Parallel.ForEachを使ってファイルに正しく書き込む方法は?

public static Object FileLock = new Object(); 
... 
Parallel.ForEach(System.IO.File.ReadLines(inputFile), (line, _, lineNumber) => 
{ 
    var output = MyComplexMethodReturnsAString(line); 
    lock (FileLock) 
    { 
     using (var file = System.IO.File.AppendText(outputFile)) 
     { 
      file.WriteLine(output); 
     } 
    } 
}); 

時間が経過してプログラムが遅くなるのはなぜですか?このタスクを実行するより正しい方法はありますか?

+0

は、あなたがに対応する出力ラインの順序が必要です:

迅速な回避策は、ParallelOptionsを受け入れそうのようなオーバーロードを使用して、参加することが許可されているスレッドParallel.ForEachの数を制限することです入力順?もしそうなら、 'Parallel.ForEach'は適切なツールではありません。 – adv12

+0

いいえ、出力行の順序は関係ありません。 – justindao

+1

私はよく分かりませんが、このようにパラレルを使用することは、IOのボトルネックを回避/回避することです。あなたがそれらの行に__really expensive__操作をしていない限り.. – TaW

答えて

4

基本的には、すべてのスレッドがファイルに書き込もうとするとクエリがシリアル化されました。代わりに、何が書かれる必要があるのか​​を計算し、最後に書いておきます。

var processedLines = File.ReadLines(inputFile).AsParallel() 
    .Select(l => MyComplexMethodReturnsAString(l)); 
File.AppendAllLines(outputFile, processedLines); 

あなたはそれが来るよう、データをフラッシュストリームを開いて、(手動またはフラッシュ)自動フラッシュ有効にする必要がある場合:これはどのようにParallel.ForEachの内部ロードバランサに関係しています

var processedLines = File.ReadLines(inputFile).AsParallel() 
    .Select(l => MyComplexMethodReturnsAString(l)); 
using (var output = File.AppendText(outputFile)) 
{ 
    output.AutoFlush = true; 
    foreach (var processedLine in processedLines) 
     output.WriteLine(processedLine); 
} 
+0

ファイルが実際にファイルである場合は、ファイル全体を最初に読み込む必要があるため、このアプローチが適切であるかどうかはわかりません。 –

+0

'File.ReadLines()'を使用していないときは、読み込まれたファイルの行を列挙する列挙可能です。これは 'File 'とは対照的です。ファイルのすべての行を含む配列を返す 'ReadAllLines()' _That_はファイル全体を読み込みます。 –

1

を働くスレッドが多くの時間を費やしていることが分かったら、問題にもっとスレッドを投げて並列処理のオーバーヘッドを増やし、FileLockの競合や全体的なパフォーマンスの低下を招くことがあります。

どうしてですか? Parallel.ForEachはIO作業のためのものではないためです。

これをどのように修正できますか? CPUの作業にのみParallel.ForEachを使用し、並列ループ外のすべてのIOを実行してください。

Parallel.ForEach(
    System.IO.File.ReadLines(inputFile), 
    new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, 
    (line, _, lineNumber) => 
    { 
     ... 
    } 
+0

私は本当にあなたの答えが「迅速な回避策」まで好きです。それは本当に前に言ったことのすべてから後退しているようです。もしあなたがコードを洗練されていれば、私にはもっと意味をなさないでしょう。 – Enigmativity

+0

好奇心:とにかく、Environment.ProcessorCountがMaxDegreeOfParallelismの自然な制限だと私は考えていました。それは間違っていますか? – TaW

+1

@TaW、いいえ、それは 'Environment.ProcessorCount'を越えて行きます。ここでは、プロセスを終了するまでに1秒間に約1スレッドを表示するフィドルがあります(私は100を超えて諦めました):https://dotnetfiddle.net/dT1eBM(言うまでもなく、あなたのプロダクションでこれを実行してはいけませんサーバ) –

関連する問題