也许有人可以指出我的方向是正确的,因为我完全是这样的.
我有一个函数,只需打印出一个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:在枚举器被实例化之后,集合被修改.
现在,您可以看到我没有修改for循环中的代码,而且我没有明确创建任何线程,尽管这在XNA环境中(如果重要).应该注意的是,打印输出是足够频繁的,以至于控制台输出整体上减慢了程序的速度.
我完全陷入困境,有没有人遇到这个?
解决方法
我怀疑开始寻找的地方将在您操纵列表的任何地方 – 即插入/删除/重新分配项目.我的怀疑是,在某个地方将会有一个回调/甚至处理程序异步地被触发(也许作为XNA绘画等循环的一部分),并且正在编辑列表 – 基本上是将这个问题作为竞争条件.
要检查是否是这种情况,请在操作列表的地方放置一些调试/跟踪输出,并查看是否有(特别是在异常之前)与控制台输出同时运行操作代码:
private void SomeCallback() { Console.WriteLine("---Adding foo"); // temp investigation code; remove components.AddLast(foo); Console.WriteLine("---Added foo"); // temp investigation code; remove }
不幸的是,这样的事情往往是调试的痛苦,因为改变代码进行调查往往会改变问题(一个Heisenbug).
一个答案是同步访问;即在编辑列表的所有地方,请使用围绕整个操作的锁定:
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
检查是否存在并插入/删除
>等
(即不是个人/离散操作 – 但是工作单位)