13

私はこれに完全に困惑しているので、おそらく誰かが私を正しい方向に向けることができます.

クラスの LinkedList を単純に出力する関数があります。

    LinkedList<Component> components = new LinkedList<Component>();
    ...
    private void PrintComponentList()
    {
        Console.WriteLine("---Component List: " + components.Count + " entries---");
        foreach (Component c in components)
        {
            Console.WriteLine(c);
        }
        Console.WriteLine("------");
    }

Componentオブジェクトには、実際には次のようなカスタム呼び出しToString()があります。

    int Id;
    ...
    public override String ToString()
    {
        return GetType() + ": " + Id;
    }

この関数は通常は正常に動作しますが、リスト内のエントリが約 30 程度になると、PrintcomplentList foreachステートメントが返されるという問題に遭遇しました。InvalidOperationException: Collection was modified after the enumerator was instantiated.

ご覧のとおり、for ループ内のコードは変更しておらず、スレッドも明示的に作成していませんが、これは XNA 環境内にあります (重要な場合)。コンソール出力がプログラム全体の速度を低下させるほど頻繁に出力されることに注意してください。

私は完全に困惑しています。他の誰かがこれに遭遇しましたか?

4

4 に答える 4

14

探し始める場所は、リストを操作する場所、つまりアイテムの挿入/削除/再割り当てであると思われます。私の疑惑は、非同期で(おそらくXNAペイントなどのループの一部として)起動され、リストを編集しているコールバック/偶数ハンドラーがどこかにあることです-本質的にこの問題を競合状態として引き起こします。

これが当てはまるかどうかを確認するには、リストを操作する場所の周りにデバッグ/トレース出力を配置し、コンソール出力と同時に操作コードを実行するかどうかを確認します(特に例外の直前)。

private void SomeCallback()
{
   Console.WriteLine("---Adding foo"); // temp investigation code; remove
   components.AddLast(foo);
   Console.WriteLine("---Added foo"); // temp investigation code; remove
}

残念ながら、コードを変更して調査すると問題が変わることが多いため、このようなことはデバッグが面倒なことがよくあります(Heisenbug)。

1つの答えは、アクセスを同期することです。つまり、リストを編集するすべてlockの場所で、完全な操作を使用します。

LinkedList<Component> components = new LinkedList<Component>();
readonly object syncLock = new object();
...
private void PrintComponentList()
{
    lock(syncLock)
    { // take lock before first use (.Count), covering the foreach
        Console.WriteLine("---Component List: " + components.Count
              + " entries---");
        foreach (Component c in components)
        {
           Console.WriteLine(c);
        }
        Console.WriteLine("------");
    } // release lock
}

そしてあなたのコールバック(または何でも)で

private void SomeCallback()
{
   lock(syncLock)
   {
       components.AddLast(foo);
   }
}

特に、「完全な操作」には次のものが含まれる場合があります。

  • カウント foreach確認して/for
  • 存在を確認し挿入/削除します

(つまり、個別/個別の操作ではなく、作業単位)

于 2009-05-10T08:41:24.343 に答える
4

の代わりにforeach、を使用してからをwhile( collection.count >0)使用しますcollection[i]

于 2010-06-24T12:37:26.887 に答える
3

これがOPに関連しているかどうかはわかりませんが、同じエラーが発生し、Google検索中にこのスレッドが見つかりました. ループ内の要素を削除した後にブレークを追加することで解決できました。

foreach( Weapon activeWeapon in activeWeapons ){

            if (activeWeapon.position.Z < activeWeapon.range)
            {
                activeWeapons.Remove(activeWeapon);
                break; // Fixes error
            }
            else
            {
                activeWeapon.position += activeWeapon.velocity;
            }
        }
    }

ブレークを省略すると、"InvalidOperationException: 列挙子がインスタンス化された後にコレクションが変更されました" というエラーが発生します。

于 2010-08-19T20:38:17.833 に答える