マルチスレッドアプリケーションのforeachループ内のアイテムを変更している関数またはターゲットを確認するにはどうすればよいですか?
「コレクションが変更されました。列挙操作が実行されない可能性があります」というエラーが引き続き表示されます。forループ内の汎用リストの項目を削除または追加していません。それがどのように変更されているのか知りたいです。これを行うための最良の方法は何ですか?
ありがとう
ちょっと待ってください-私たちは皆、要点を逃したと思います。
コレクションクラスはスレッドセーフではありません。コレクションがスレッドロックなしで複数のスレッドによってアクセスされている場合は、それを修正する必要があります。これは特に陰湿な問題です。99.999%の確率で正しく動作し[編集-または予測可能な例外をスロー]、0.001%の確率で完全に予測不可能で再現がほぼ不可能なことを実行します。
簡単なアプローチは、任意のコードがコレクションにアクセスするすべての場所でlock{}ステートメントを使用することです。それは少しやり過ぎかもしれませんが、それは最も安全な計画です。反復の場合、ループ全体をロックするか、他のスレッドをそれほど長くブロックしたくない場合は、スナップショットを作成するのに十分な時間ロックするだけです。
object[] snap;
lock (list)
{
snap = list.ToArray();
}
foreach (object x in snap) ...
これは直接的な答えではなく、役立つ可能性のある提案です。反復中にコレクションが変更される可能性があるマルチスレッドの状況では、通常、ToArrayメソッドを使用して反復用のリストのスナップショットを作成します。
foreach (object x in list.ToArray()) ...
コレクションをObservableCollectionに置き換え、イベントを変更にアタッチして、デバッガーでそれらを中断します(またはスタックトレースなどをキャプチャします)。
これを行う最良の方法は、コレクションを変更する可能性のある多くのクラスにコレクションを公開しないことです。特定のクラスに対してプライベートにすると、そのクラスのメソッドのみが変更できる可能性があります。
それ以外の場合は、コレクションクラスから派生クラスを作成し、コレクションを更新する可能性のあるすべてのメソッドをオーバーライドする必要があります。すべてのオーバーライドにブレークポイントを設定します。
グローバルコレクションの内容をより厳密にスコープされた変数にコピーして、データとの外部の干渉を保護することは可能でしょうか?
例外ブレークポイントを設定できるはずです。そうすれば、各スレッドを調べて、どのスレッドがコレクションを変更したかを確認するだけです。いずれの場合も、コレクションが複数のスレッド間で共有されている場合は、適切に同期する必要があります。
このためにObservableCollectionクラスを試してみることをお勧めします。このスレッドを安全にするための例もいくつかあります。
コレクションのクラス名を変更して、すべてが参照されている場所を確認することもできます。