2

を使用して、私はこのコードを持っている:予期しない動作Parallel.Foreach

// positions is a List<Position> 
Parallel.ForEach(positions, (position) => 
{ 
    DeterminePostPieceIsVisited(position, postPieces); 
}); 

private void DeterminePostPieceIsVisited(Position position, IEnumerable<Postpieces> postPieces) 
{ 
    foreach (var postPiece in postPieces) 
    { 
     if (postPiece.Deliverd) 
      continue; 

      var distanceToClosestPosition = postPiece.GPS.Distance(position.GPS); 

      postPiece.Deliverd = distanceToClosestPosition.HasValue && IsInRadius(distanceToClosestPosition.Value); 
     } 
    } 
} 

は、私は50個のポスト片がtrueにプロパティDeliverdセットを持たなければならないことを知っています。しかし、このコードを実行すると、結果が変化します。時々私は44になり、別の時間に実行すると47になります。結果は実行ごとに異なります。

普通のforeachループを使用してこのコードを実行すると、予想される結果が得られます。だから私はDeterminePostPieceIsVisitedメソッドの実装が正しいことを知っています。

誰かが私にこのコードを実行するたびに異なる結果を与えるParallel foreachを使用する理由を説明できますか?同じコレクションが複数のスレッドによって変更されるよう、原因のデータが破損する結果に差が生じる競合状態を引き起こしている、次の並列コードで

+1

すべての並行作業者に、同じ* 'postPieces'コレクションを反復するように求めています。スレッドセーフでないオブジェクトに複数のライターを抱えていると、問題が発生することがあります。 – AakashM

+0

これを解決する解決策は何でしょうか? – Martijn

+0

なぜこのような場合に 'Parallel.ForEach'を使用していますか? – user3185569

答えて

-1

:私はそれはのように動作させるために考えることができます

postPiece.Deliverd = distanceToClosestPosition.HasValue && IsInRadius(distanceToClosestPosition.Value); 

簡単な方法今のコードのlockこの作品です:

public static readonly Object lockObj = new Object(); 

lock(lockObj) 
{ 
    postPiece.Deliverd = distanceToClosestPosition.HasValue && IsInRadius(distanceToClosestPosition.Value); 

} 

は、私はより良いデザインを用意している場合、ここでの課題は、すべてのスレッドがコレクションに同時に上で作業してオブジェクト

012を更新している、私は考えてみましょう
IEnumerable<Postpieces> postPieces 

理想的には、すべてのスレッドは、データ破損や競合状態を回避するために、独自のデータセットで作業する必要があります。これは予期しない動作でもありません。あなたはすでに、私が思うに、レースを避けるためにしようとしましたが、一つはまだある

+0

*コレクション*はまったく変更されていません。コレクション内に*入っている個々のオブジェクトのプロパティは変更されていますが、それは別の問題です。 –

+0

合意されていますが、コレクションが変更されていても不変であれば、新しいコレクションを作成していました。この場合、OPは現在のコレクションオブジェクトを変更したいときに、複数のスレッドで実行します。 –

+0

現在の質問に対するベストソリューション、複数のスレッドが動作するようにデータをスライスすることができれば、小さなデータのサブセットです。現在、同じコレクションで作業しているすべてのスレッドはそれ自体が難しいです。 –

3

- 2つのスレッドが同時に同じpostPieceを検討しているならば、彼らは両方Deliverd(原文のまま)がfalseであることを観察することができますそれから、position(各スレッドの個別の値)に配信されているかどうかを評価し、両方ともDeliverdの値を設定しようとします。しばしば、そのうちの1つがfalseに設定しようとしています。簡単な修正:

private void DeterminePostPieceIsVisited(Position position, IEnumerable<Postpieces> postPieces) 
{ 
    foreach (var postPiece in postPieces) 
    { 
     if (postPiece.Deliverd) 
      continue; 

     var distanceToClosestPosition = postPiece.GPS.Distance(position.GPS); 

     var delivered = distanceToClosestPosition.HasValue && IsInRadius(distanceToClosestPosition.Value); 
     if(delivered) 
      postPiece.Deliverd = true; 
    } 
} 

また、方法によって:

私は無地のforeachループを使用してこのコードを実行すると、私は期待される結果を得ます。だから私はDeterminePostPieceIsVisitedメソッドの実装が正しいことを知っています。

状態に正しいものは次のようになりますです「私は私の実装では、シングルスレッドのアクセスため正しいことを知っている」 - 何が確立していなかったことは方法は、複数のスレッドから呼び出すために安全だったということです。

+0

良い解決策、私だけの心配私は同じメモリの場所で、これは避けることはできませんいずれかのスレッドを複数の書き込みですが、確かに競合状態につながることはありません、データの破損とロックを避けるが、負荷が高い場合それはメモリ破壊を引き起こします。何らかの種類のアクセス違反の可能性があります。 –

+0

@MrinalKamboj - Deliverdがフィールドまたはフィールドの些細な設定者である限り、ここにはメモリ破損の問題はありません。 'bool'への書き込みは原子的です(https://msdn.microsoft.com/en-us/library/aa691278(v = vs.71).aspx) –

+0

@ Damien_The_Unbelieverどのようにコード' postPiece.Deliverd = true; '、ヒープ上で割り当てられ、変更されました。 –

0

ConcurrentBag<T>で問題を解決しました。ここで私が今使っていることは次のとおりです:

var concurrentPostPiecesList = new ConcurrentBag<Postpiece>(postPieces); 
Parallel.ForEach(positions, (position) => 
{ 
    DeterminePostPieceIsVisited(position, concurrentPostPiecesList); 
}); 

private void DeterminePostPieceIsVisited(Position position, ConcurrentBag<Postpieces> postPieces) 
{ 
    foreach (var postPiece in postPieces) 
    { 
     if (postPiece.Deliverd) 
      continue; 

     var distanceToClosestPosition = postPiece.GPS.Distance(position.GPS); 

     postPiece.Deliverd = distanceToClosestPosition.HasValue && IsInRadius(distanceToClosestPosition.Value); 
    } 
} 
+0

あなたは、私は、スレッドセーフなデータ構造を提案しました。 IEnumerableのソースと、Order/Indexingが必要かどうかを認識していないので、この答えを示唆するのは難しいですが、これはConcurrentBagでは不可能です –