2009-04-21 245 views
2

如何找出哪个函数或目标正在修改多线程应用程序的foreach循环中的项目?查找如何修改foreach集合

我不断收到错误“Collection was modified; enumeration operation may not execute”。我不会在for循环中删除或添加任何项目到通用列表中。 我想知道它是如何被修改的。 这样做的最好方法是什么?

感谢

+0

这个类,其中包含foreach循环是通用列表的一部分。 除此之外,该通用列表中的此类的每个实例都在单独的线程中运行 – Josh 2009-04-21 13:23:52

回答

7

等一下 - 我想我们都错过了这一点。

集合类不是线程安全的。如果集合正在被多线程访问而没有线程锁定,那么你需要修复这个问题。这是一个特别隐秘的问题,因为它会正常工作[编辑 - 或抛出一个可预见的例外] 99.999%的时间,0.001%的时间,它会做一些完全不可预测,几乎不可能重现的事情。

一个简单的方法是在任何代码访问集合的每个地方使用lock {}语句。这可能有点矫枉过正,但这是最安全的计划。对于迭代,你可以把一个锁周围的整个循环,或者如果你不想阻挡其他线程那么久,只是锁定它足够长的时间,使快照:

object[] snap; 
lock (list) 
{ 
    snap = list.ToArray(); 
} 
foreach (object x in snap) ... 
+1

并发不需要解决此问题。循环内调用的方法可能会导致修改集合的代码。 – Richard 2009-04-21 14:30:14

0

这样做是为了收集不暴露在许多类这有可能改变它的最好办法。使它对一个特定的类是私有的,那么只有该类的方法才可能改变它。

否则,您需要从集合类创建派生类,并覆盖所有可更新集合的方法。在所有覆盖中设置断点。

0

将全局集合的内容复制到更紧密的范围变量以保护外部数据干预是否可行?

2

这不是一个直接的答案,只是一个建议,可能会帮助 - 在多线程的情况下收集可能迭代过程中得到修改,我一般使用的ToArray方法创建列表的迭代快照:

foreach (object x in list.ToArray()) ... 
1

将集合替换为ObservableCollection,将事件附加到修改中,并在调试器(或捕获堆栈跟踪等)中打断它们。

0

你应该能够设置一个异常断点,然后只需要通过每个线程看看哪一个修改了这个集合。无论如何,如果该集合在多个线程之间共享,则需要进行适当的同步。

0

您也可以尝试更改集合的类名,以查看其全部引用的位置。