11

マルチスレッドアプリケーションがあり、このエラーが発生します

************** Exception Text **************
System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
   at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
   at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
   at System.Collections.Generic.List`1.Enumerator.MoveNext()
   ...

あるスレッドでコレクションを読み取り、別のスレッドでコレクションを変更するため、コレクションに問題がある可能性があります。

public readonly ObservableCollectionThreadSafe<GMapMarker> Markers = new ObservableCollectionThreadSafe<GMapMarker>();


public void problem()
{
  foreach (GMapMarker m in Markers)
  {
    ...
  }
}

このコードでコレクションをロックしようとしていますが、機能しません。

public void problem()
    {
       lock(Markers)
       {
         foreach (GMapMarker m in Markers)
         {
           ...
         }
       }
    }

その問題を解決するためのアイデアはありますか?

4

6 に答える 6

14

これはかなりよくある間違いです。 を使用して反復しながらコレクションを変更します。読み取り専用インスタンスforeachを使用することに注意してください。foreachIEnumerator

追加のインデックス チェックを使用してコレクションをループしてみてくださいfor()。インデックスが範囲外の場合は、追加のロジックを適用してそれを処理できます。基になる列挙が実装されていない場合は、毎回値を評価することにより、 LINQCount()を別のループ終了条件として使用することもできます。CountICollection

Markers実装する場合IColletion- SyncRoot をロックします。

lock (Markers.SyncRoot)

使用for():

for (int index = 0; index < Markers.Count(); index++)
{
    if (Markers>= Markers.Count())
    {
       // TODO: handle this case to avoid run time exception
    }
}

この投稿が役に立つかもしれません: How do foreach loops work in C#?

于 2012-03-29T12:17:41.887 に答える
4

読み取り側と書き込み側の両方でロックする必要があります。そうしないと、スレッドの 1 つがロックを認識せず、コレクションの読み取り/変更を試みますが、もう 1 つのスレッドはロックを保持したまま (それぞれ) 変更/読み取りを行っています。

于 2012-03-29T12:16:36.763 に答える
4

コレクションのクローンを読み取ってみてください

foreach (GMapMarker m in Markers.Copy())
{
   ...
}

これにより、別のスレッドの影響を受けないコレクションの新しいコピーが作成されますが、膨大なコレクションの場合にパフォーマンスの問題が発生する可能性があります。

そのため、読み書き処理中はコレクションをロックした方が良いと思います。

于 2012-03-29T12:17:37.127 に答える