集合被修改,枚举操作可能无法执行
Posted
技术标签:
【中文标题】集合被修改,枚举操作可能无法执行【英文标题】:Collection was modified, enumeration operation may not execute 【发布时间】:2012-04-13 01:58:51 【问题描述】:我有多线程应用程序,但出现此错误
************** Exception Text **************
System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
at System.Collections.Generic.List`1.Enumerator.MoveNext()
...
我的收藏可能有问题,因为在一个线程上我阅读了我的收藏,而在另一个线程上我修改了收藏。
public readonly ObservableCollectionThreadSafe<GMapMarker> Markers = new ObservableCollectionThreadSafe<GMapMarker>();
public void problem()
foreach (GMapMarker m in Markers)
...
我正在尝试使用此代码锁定集合,但不起作用。
public void problem()
lock(Markers)
foreach (GMapMarker m in Markers)
...
有解决这个问题的想法吗?
【问题讨论】:
你的问题是foreach
里面的代码,请贴出来。
使用 foreach 循环时不能修改集合
【参考方案1】:
这对我有用。对标记执行 ToList() 操作:
foreach (GMapMarker m in Markers.ToList())
【讨论】:
【参考方案2】:这是一个很常见的错误 - 在使用 foreach
迭代集合的同时修改集合,请记住 foreach
使用只读的 IEnumerator
实例。
尝试使用for()
循环遍历集合并进行额外的索引检查,因此如果索引超出范围,您将能够应用额外的逻辑来处理它。如果底层枚举没有实现ICollection
,您还可以通过每次评估Count
值来使用LINQ 的Count()
作为另一个循环退出条件:
如果 Markers
实现 IColletion
- 锁定 SyncRoot:
lock (Markers.SyncRoot)
使用for()
:
for (int index = 0; index < Markers.Count(); index++)
if (Markers>= Markers.Count())
// TODO: handle this case to avoid run time exception
您可能会发现这篇文章很有用:How do foreach loops work in C#?
【讨论】:
但是如果修改是通过从集合中删除一个项目,那将抛出一个IndexOutOfRange
异常
我提到了额外的索引检查以避免这个问题,将添加示例,感谢您指出这一点
我想用 for 替换 foreach 但后来我想如果锁定集合更好但不起作用:/
int i = mapMarkers.Markers.IndexOf(oldMarker); if (i != -1) mapMarkers.Markers[i] = newMarker;
由于ObservableCollectionThreadSafe
是您的自定义集合类,请显示添加/删除的代码,顺便说一句,您是否在添加/删除方法中锁定了this.SyncRoot
?【参考方案3】:
我通过使用解决了这个问题
var data = getData();
lock(data)
return getData().Select(x => new DisplayValueModel(x));
而不是
return getData().Select(x => new DisplayValueModel(x));
【讨论】:
【参考方案4】:您可以使用 foreach,但您必须将集合转换为列表并使用点运算符来访问行为方法。
示例:Markers.Tolist().ForEach(i => i.DeleteObject())
不完全确定你在用你的收藏做什么。我的示例假设您只想从集合中删除所有项目,但它可以应用于您尝试对集合执行的任何行为。
【讨论】:
【参考方案5】:尝试读取您收藏的副本
foreach (GMapMarker m in Markers.Copy())
...
这将为您的集合创建一个新副本,该副本不会受到另一个线程的影响,但在大量集合的情况下可能会导致性能问题。
所以我认为如果你在读写过程中锁定集合会更好。
【讨论】:
...修改原始收藏。 你说得对,我认为使用.Copy
但这可能会导致性能问题。【参考方案6】:
您需要同时锁定读取和写入端。否则,其中一个线程将不知道锁并尝试读取/修改集合,而另一个线程则在持有锁的情况下(分别)修改/读取
【讨论】:
以上是关于集合被修改,枚举操作可能无法执行的主要内容,如果未能解决你的问题,请参考以下文章
System.invalidoperationexception:集合被修改;枚举操作可能无法在 .net 5 api 项目中执行