質問: 自作ゲームで木構造のシーングラフを持っています。 親ノードは複数の子ノードを持ちゲームの進行に応じてノードが追加されたり削除されたりします。ようはコレクションの変更です。async/await を使った非同期プログラミングでこれを行うとタイミングによっては `foreach` でぐるぐる回している最中に別スレッドからコレクションを変更することになり例外が発生します(これ自体は当然の動作)。 質問は非同期プログラミングでのコレクション操作のベストな方法を教えて下さい、という事です。 1. `SemaphoreSlim` で排他制御を行う 2. コレクションを必ず `ToArray()` でコピーしてから使う 3. 追加および削除を1つのスレッドで行うようにロジックを変更する どれも一長一短で決めかねています。ご意見をお聞かせ下さい。 --- 回答を考慮して追加 4. `System.Collections.Concurrent` 名前空間のスレッドセーフなコレクションを使用する 5. `System.Collections.ImmutableList` 名前空間の不変なコレクションを使用する 4.はConcurrentBag, ConcurrentStack, ConcurrentQueue, ConcurrentDictionaryなど。既存のListに比べて呼び出し方法が大分異なる。基本はキューとスタックなのでProducer-Consumerパターン。 5.は.Net標準ではないがマイクロソフト提供のライブラリ。NuGetでインストール可能。「不変」(Immutable)なコレクションなのでマルチスレッド環境でも問題が起きない。コレクションの変更はコピーして変更を加えて差し替えるので比較的コストが高い。参照するだけなら問題はない。 排他制御がコードに現れない、既存のListとほぼ同様のインターフェースを持つ、書き換えはそんなに起きないので無視できる、などを評価して5番のImmutableListがベストですね。 今のところ.Net標準ではないですがそのうち標準として取り込まれるような気がします。これは便利だ。
回答: System.Collections.Concurrent名前空間のコレクションを使用します。 この名前空間に属するコレクションは、非同期に同時アクセス可能です。 lockする必要は、ありません。 もう一つの方法としては、System.Collections.Immutable名前空間に所属する 不変コレクションを使用します。 不変コレクションは、コレクションの変更を行うと新しいコレクションへの参照が作成されます。 System.Collections.Immutableは、標準では利用できないかも知れません。 nuget経由で導入することができます。 これらのコレクションを使用すると、lockフリーにプログラミング可能になります。 列挙操作中にコレクションを変更しても例外は、発生しません。