オブジェクトの汎用 IList<> を列挙する必要があります。他のスレッドによって追加または削除されるなど、リストの内容が変更される可能性があり、これにより、「コレクションが変更されました。列挙操作が実行されない可能性があります」というメッセージが表示されて列挙が強制終了されます。
IList<>でスレッドセーフなforeachを行う良い方法は何ですか? できればリスト全体を複製しないでください。リストによって参照される実際のオブジェクトを複製することはできません。
オブジェクトの汎用 IList<> を列挙する必要があります。他のスレッドによって追加または削除されるなど、リストの内容が変更される可能性があり、これにより、「コレクションが変更されました。列挙操作が実行されない可能性があります」というメッセージが表示されて列挙が強制終了されます。
IList<>でスレッドセーフなforeachを行う良い方法は何ですか? できればリスト全体を複製しないでください。リストによって参照される実際のオブジェクトを複製することはできません。
リストのクローンを作成するのが最も簡単で最良の方法です。これにより、リストが自分の下から変更されないようになります。リストが大きすぎて複製できない場合は、リストの読み取り/書き込みの前に取得する必要のあるロックをリストの周りに配置することを検討してください。
そのような操作はありません。あなたができる最善のことは
lock(collection){
foreach (object o in collection){
...
}
}
問題は、列挙によってIListを変更できないことです。これは、リストを確認するときにこれを回避する必要があることを意味します。
いくつかの可能性が思い浮かびます。
Alternatively, you could write your own implementation of IList and IEnumerator that allows the kind of parallel access you need. However, I'm afraid this won't be simple.
したがって、要件は次のとおりです。要素を同時に追加および削除しながら、コピーを作成せずに IList<> を列挙する必要があります。
いくつか明確にしていただけますか?挿入と削除は、リストの最初または最後でのみ行われていますか? リスト内の任意のポイントで変更が発生する可能性がある場合、列挙の現在の要素の場所またはその近くで要素が削除または追加されたときに、列挙はどのように動作する必要がありますか?
これは、おそらく整数インデックスを持つカスタム IEnumerable オブジェクトを作成することで確実に実行できますが、IList<> オブジェクトへのすべてのアクセスを制御できる場合に限ります (列挙の状態をロックおよび維持するため)。しかし、マルチスレッド プログラミングは、最善の状況下ではトリッキーなビジネスであり、これは複雑な可能性です。
非常に興味深いトピックであることがわかります。
最善のアプローチは、いわゆるコンボイ問題による大きなパフォーマンスの問題を抱えていた ReadWriteResourceLock に依存しています。
このテーマを扱っている記事として私が見つけた中で最も優れているのは、Jeffrey Richter によるこの記事で、高性能ソリューションのための独自の方法を公開しています。
ICollection MyCollection;
// Instantiate and populate the collection
lock(MyCollection.SyncRoot) {
// Some operation on the collection, which is now thread safe.
}
MSDNから
Forech は、コレクションが変更されないという事実に依存します。変化する可能性のあるコレクションを反復処理する場合は、通常の構成を使用し、非決定的な動作に備えてください。何をしているかによっては、ロックする方が良いかもしれません。
リンク リスト、B ツリー、またはハッシュ テーブルのような単純なインデックス付きデータ構造の既定の動作は、最初から最後まで順に列挙することです。イテレータがすでにそのポイントを通過した後にデータ構造に要素を挿入したり、イテレータが到着した後に列挙する要素を挿入したりしても、問題は発生しません。アプリケーションはそれを必要としました。コレクションの変更を検出し、列挙中にエラーをスローすることは、プログラマーが望んでいることを実行するという誰かの (悪い) 考えであるとしか想像できませんでした。実際、Microsoft はコレクションが正しく機能するように修正しました。.NET 4.0 では、光沢のある新しい連続したコレクションを ConcurrentCollections (System.Collections.Concurrent) と呼んでいます。
読み取りと書き込みのために、リストをロックオブジェクトでラップします。適切なロックがあれば、一度に複数のリーダーで反復することもできます。これにより、複数の同時リーダーだけでなく、単一のライター(リーダーがない場合)も可能になります。
これは私が最近対処しなければならなかったことであり、私にとっては、リストで何をしているかに大きく依存しています。
ある時点でリストを使用する必要があり (現在リストにある要素の数が与えられている場合)、別のスレッドがリストの最後にしか追加できない場合は、カウンターを使用して FOR ループに切り替えるだけです。カウンターをつかんだ時点では、リストには X 個の要素しか表示されていません。リストをウォークスルーできます (他の人がリストの最後に追加している間) 。. . 問題を引き起こすべきではありません。
ここで、他のスレッドによってリストからアイテムを取り出したり、他のスレッドによってクリアしたりする必要がある場合は、上記のロック メカニズムのいずれかを実装する必要があります。また、新しい「並行」コレクション クラスのいくつかを確認することもできます (ただし、それらが IList を実装しているとは思わないため、辞書のリファクタリングが必要になる場合があります)。