2

Parallel.ForEach について本当に混乱しています...どのように機能しますか?
以下のコードにはエラーがあります ->File Is In Use

Parallel.ForEach(list_lines_acc, (line_acc, list_lines_acc_state) =>
{
     FileStream file = 
         new FileStream(GPLfilePath, FileMode.Open, FileAccess.ReadWrite);
     StreamReader reader = new StreamReader(file);
     var processed = string.Empty;
     Ok_ip_port = string.Empty;
     while (reader.EndOfStream)
     {
         if (string.IsNullOrEmpty(Ok_ip_port))
         {
             Ok_ip_port = reader.ReadLine();
         }
         else
         {
             string currentLine = reader.ReadLine();
             processed += currentLine + Environment.NewLine;
         }
     }
     StreamWriter writer = new StreamWriter(file);
     writer.Write(processed);

     reader.Close();
     writer.Close();
     file.Close();
});  

どうすれば修正できるか教えてください。このコードは単なる例です。

Parallel.ForEach 内で文字列配列とリストを操作したいのですが、これらのコレクションを追加または編集するには常に問題があります。例を教えてください。Visual Studio 2010 + .NET Framework 4.0 を使用しています

4

4 に答える 4

7

あなたのコードでは、書かれているように、各スレッドは同じファイルを使用しており、効果的にそのファイルに追加しようとしています。これが機能する可能性があるとしても、競合状態が悪くなります(スレッドが同じファイルに同時に追加しようとするため)。

表示されているエラーは、純粋に各ループの反復で同じファイルを使用しているためです。そのため、(最初の反復の後で)ファイルを開こうとすると、別のループの反復で開かれるため、エラーが発生します。

また、ループ変数(line_acc)を使用することはないため、ここではループはまったく必要ありません。これは、なしで記述でき、Parallel.ForEach問題なく同じ結果が得られます。

そうは言っても、これがサンプルコードである場合、ファイルI/Oによって純粋にバインドされているループはうまく並列化されない傾向があることに気付くでしょう。使用されている実際のドライブが制限要因になるため、ファイルの読み取りと書き込みを純粋に並行して実行するコードを実行すると、結果のコードの実行速度が、順次実行するよりも遅くなることがよくあります。

Parallel.ForEach内で文字列配列とリストを操作したいのですが、これらのコレクションの追加または編集には常に問題があります

「例として」表示しているコードはこれを何も行っていないため、問題が発生している可能性のある場所を確認するのは困難です。配列またはインデックスで書き込むことはできますが、書き込みに対してスレッドセーフではないためList<T>、追加の同期(などlock)なしで並列ループのリストに追加することはできません。List<T>コレクションからの読み取りと書き込みを行おうとしている場合は、ループSystem.Collections.Concurrentで安全に使用できるスレッドセーフなコレクションが含まれている名前空間を調べることを検討してください。Parallel.ForEach

于 2012-09-24T18:32:37.197 に答える
2

この質問に記載されているように:

インデックスへのアクセスを同期していないため、競合が発生しています。そのため、エラーが発生します。説明のために、Interlocked.Increment を使用して競合を回避し、この特定の設計を維持できます。

private static void Func<T>(IEnumerable<T> docs)
{
    int index = -1;
    Parallel.ForEach(
        docs, doc =>
        {
            int nextIndex = Interlocked.Increment(index);
            CreateFolderAndCopyFile(nextIndex);
        }
    );
}

ただし、他の人が示唆しているように、ループ インデックスを提供する ForEach の代替オーバーロードは、この特定の問題に対する明確な解決策です。

しかし、それが機能するようになると、ファイルのコピーがプロセッサ バウンドではなく IO バウンドであることがわかり、並列コードはシリアル コードよりも遅くなると予測されます。

于 2012-09-24T18:16:34.180 に答える
1

問題のあるコードの周りにオブジェクトを使用してくださいlock...実行はロックが解放されるのを待ち、複数のスレッドがリソースにアクセスすることは決してありません....この場合、並列ForEachはパフォーマンスを追加しません。以下に簡単な例を示します。

private Object fileLock = new Object();
private void WriteLog(string line)
{
    lock (fileLock)
    {
        string strNomLog = @".\MyFile.log";
        System.IO.File.AppendAllText(strNomLog, line);
    }
}
于 2015-03-25T14:01:29.897 に答える
0

使用中のファイルのエラーを取り除くには(別のスレッドがファイルに書き込んでいるために使用中であると想定して)、ファイルへのアクセスを同期する必要があります。これは通常、各並列実行が他の実行を待機して書き込みを終了し、並列実行の目的を無効にすることを意味します。

于 2012-09-24T18:28:17.467 に答える