2

次のコード スニペットがあります。

lock (lockObject) 
{
  foreach (KeyValuePair<string, List<string>> entry in productDictionary) 
  {
    string writePath = String.Format(@"{0}\{1}-{2}.txt", directoryPath, hour,entry.Key);
    Task writeFileTask = Task.Factory.StartNew(() => WriteProductFile(writePath, entry.Value));
  }
}

productDictionary は、ConcurrentDictionary<string, List<string>>が反復しようとしているものです。キーと値のペアごとに、に基づいてファイル パスをKey作成し、に格納されている文字列のリストを書き出そうとしていValueます。この目的のために、次のメソッドを呼び出す新しいタスクを開始します。

public static void WriteProductFile(string filePath, List<string> productQuotes)
{
    using(StreamWriter streamWriter = new StreamWriter(filePath)) 
    {
        productQuotes.ForEach(x => streamWriter.WriteLine(x));
    }
}

コードをステップ実行すると、最初はすべて問題ないように見えます。メソッド呼び出しにブレークポイントを設定するWriteProductFileと、正しいパラメーターが Task を介してメソッドに渡されていることがわかります。ただし、私のプログラムが実際に WriteProductFile メソッドに到達すると、パラメーターが一致しなくなりました。つまり、ファイルパスに一致しない文字列のリストが渡されたため、プログラムが完了するとデータが役に立たなくなります。エラーはスローされず、プログラムは正常に実行されますが、間違った情報が間違ったファイルに書き出されています。

ConcurrentDictionary と Lock は、発生するスレッド化の問題を処理すると思っていましたが、どうやら何かを見逃していたようです。何か案は?

4

1 に答える 1

3

ループ変数をキャプチャしています。ループ内で local in を宣言する必要があります。

foreach (KeyValuePair<string, List<string>> entry in productDictionary) 
{
  string writePath = String.Format(@"{0}\{1}-{2}.txt", directoryPath, hour, entry.Key);
  List<string> list = entry.Value; // must add this.
  Task writeFileTask = Task.Factory.StartNew(() => WriteProductFile(writePath, list));
}

C# 5 より前では、ループのすべての繰り返しに単一のループ変数が使用されていました。各クロージャーは同じ変数を参照するため、ループの新しいサイクルが開始されると値が更新されます。決定的な説明については、Eric Lippert の投稿を読む必要があります

于 2012-11-14T17:01:36.987 に答える