集合被修改,枚举操作可能无法执行

时间:2012-03-29 12:14:47

标签: c# multithreading enumerator

我有多线程应用程序,我收到此错误

************** 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)
         {
           ...
         }
       }
    }

解决这个问题的任何想法?

5 个答案:

答案 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));