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

本文关键字:执行 操作 修改 枚举 集合 | 更新日期: 2023-09-27 18:29:19

我有多线程应用程序,我得到了这个错误

************** 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使用只读IEnumerator实例。

尝试使用带有额外索引检查的for()循环遍历集合,这样,如果索引越界,您就可以应用额外的逻辑来处理它。如果底层枚举没有实现ICollection:,您还可以通过每次评估Count值来使用LINQ的Count()作为另一个循环退出条件

如果Markers在SyncRoot:上实现IColletion锁定

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
    }
}

你可能会发现这篇文章很有用:foreach循环在C#中是如何工作的?

您需要在读取端和写入端进行锁定。否则,其中一个线程将不知道锁,并将尝试读取/修改集合,而另一个线程则使用锁持有的来修改/读取(分别)

尝试读取集合的克隆

foreach (GMapMarker m in Markers.Copy())
{
   ...
}

这将创建一个新的集合副本,该副本不会受到另一个线程的影响,但在大量集合的情况下可能会导致性能问题。

所以我认为如果你在阅读和写作过程中锁定收藏会更好。

这对我很有效。对Markers执行ToList()操作:foreach (GMapMarker m in Markers.ToList())

您可以使用foreach,但必须将集合强制转换为列表,并使用点运算符访问行为方法。

示例:Markers.Tolist().ForEach(i=>i.DeleteObject())

不完全确定你在用你的收藏品做什么。我的例子是假设您只想从集合中删除所有项目,但它可以应用于您试图对集合执行的任何行为。