2011-01-13 5 views
37

新しいタスクで最初に試してみたいが、わからないことが起こっている。foreachループでタスクを開始する最後のアイテムの値を使用する

まず、コードはかなり簡単です。私はいくつかの画像ファイルへのパスのリストを渡し、およびそれらのそれぞれを処理するためにタスクを追加しよう:私が見つけた

public Boolean AddPictures(IList<string> paths) 
{ 
    Boolean result = (paths.Count > 0); 
    List<Task> tasks = new List<Task>(paths.Count); 

    foreach (string path in paths) 
    { 
     var task = Task.Factory.StartNew(() => 
      { 
       Boolean taskResult = ProcessPicture(path); 
       return taskResult; 
      }); 
     task.ContinueWith(t => result &= t.Result); 
     tasks.Add(task); 
    } 

    Task.WaitAll(tasks.ToArray()); 

    return result; 
} 

私はちょうど言う、このラン、3のリストを許可すればということ3つのタスクすべてが提供されたリストの最後のパスを使用します。ステップスルーしてループの処理を遅くすると、ループからの各パスが使用されます。

誰かが何が起こっているのか、なぜ説明できますか?考えられる回避策?あなたはStartNewに渡している

+3

私はReSharperのを使用して提案することができる。この特定のエラーやその他の潜在的なバグが –

答えて

73

ループ変数をクローズしています。それをしないでください。代わりにコピーしてください:

foreach (string path in paths) 
{ 
    string pathCopy = path; 
    var task = Task.Factory.StartNew(() => 
     { 
      Boolean taskResult = ProcessPicture(pathCopy); 
      return taskResult; 
     }); 
    task.ContinueWith(t => result &= t.Result); 
    tasks.Add(task); 
} 

あなたの現在のコードはpathを捕捉している - ではないタスクを作成することのが、変数自体を。その変数は、ループを通過するたびに値を変更するため、デリゲートが呼び出されるまでに簡単に変更できます。変数のコピーを取ることによって

、あなたがループを通過するたびに新しい変数を導入している - あなたはその変数をキャプチャするとき、それがループの次の反復で変更されることはありません。

エリックリッペルトには、ブログの記事がたくさん掲載されています。part 1; part 2

は気を悪くしないでください - これは、ほとんどの人をキャッチ:(

+1

あなたのためしかし、もちろんhighlightenされている森の木々やすべてのことのために。。。 :) –

+1

この閉鎖の問題とRandom()の適切な使用は、SO – BrokenGlass

+0

でトップ5になければなりません。この "バグ"(もともと*設計上*だった)はC言語で修正されているはずです#5.0 –

12

ラムダは、各反復で変化path変数を参照している(つまり、あなたのラムダは参照pathだけではなく、その値を利用しています)。ローカルコピーを作成して、変更するバージョンを指していないようにすることができます。

foreach (string path in paths) 
{ 
    var lambdaPath = path; 
    var task = Task.Factory.StartNew(() => 
     { 
      Boolean taskResult = ProcessPicture(lambdaPath); 
      return taskResult; 
     }); 
    task.ContinueWith(t => result &= t.Result); 
    tasks.Add(task); 
} 
関連する問題