我有多线程应用程序,我收到此错误
************** 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)
{
...
}
}
}
解决这个问题的任何想法?
答案 0 :(得分:11)
这是一个非常常见的错误 - 在使用foreach
进行迭代时修改集合,请记住foreach
使用只读IEnumerator
实例。
尝试使用for()
使用额外的索引检查来遍历集合,因此如果索引超出范围,您将能够应用其他逻辑来处理它。如果底层枚举未实现Count()
,则每次评估Count
值时,也可以使用LINQ的ICollection
作为另一个循环退出条件:
如果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#?
答案 1 :(得分:4)
您需要同时锁定读数和写入方面。否则其中一个线程将不知道锁定并将尝试读取/修改集合,而另一个线程正在修改/读取(分别)锁定
答案 2 :(得分:4)
尝试阅读集合的克隆
foreach (GMapMarker m in Markers.Copy())
{
...
}
这将创建您的集合的新副本,该副本不会受到另一个线程的影响,但在收集大量数据时可能会导致性能问题。
所以我认为如果你在阅读和写作过程中锁定了这个集合会更好。
答案 3 :(得分:0)
您可以使用foreach但是必须将集合强制转换为列表并使用点运算符来访问行为方法。
示例:Markers.Tolist()。ForEach(i =&gt; i.DeleteObject())
不完全确定您对收藏品的处理方式。我的例子是假设您只想删除集合中的所有项目,但它可以应用于您尝试对集合执行的任何行为。
答案 4 :(得分:0)
我通过使用
解决了这个问题var data = getData();
lock(data)
{
return getData().Select(x => new DisplayValueModel(x));
}
代替
return getData().Select(x => new DisplayValueModel(x));