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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9925083/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 11:15:22  来源:igfitidea点击:

Collection was modified, enumeration operation may not execute

c#multithreadingenumerator

提问by PATO7

I have multithreads application and i get this error

我有多线程应用程序,但出现此错误

************** 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()
   ...

I probably have problem with my collection, because on one thread i read my collection and on another thread i modify collection.

我的收藏可能有问题,因为在一个线程上我阅读了我的收藏,而在另一个线程上我修改了收藏。

public readonly ObservableCollectionThreadSafe<GMapMarker> Markers = new ObservableCollectionThreadSafe<GMapMarker>();


public void problem()
{
  foreach (GMapMarker m in Markers)
  {
    ...
  }
}

I am trying to lock collection with this code, but doesn't work.

我正在尝试使用此代码锁定集合,但不起作用。

public void problem()
    {
       lock(Markers)
       {
         foreach (GMapMarker m in Markers)
         {
           ...
         }
       }
    }

Any ideas to fix that problem?

有什么想法可以解决这个问题吗?

回答by Attila

You need to lock both on the reading and the writing side. Otherwise one of the threads will not know about the lock and will try to read/modify the collection, while the other is modifying/reading (respectively) with the lock held

你需要在阅读和写作方面都锁定。否则,其中一个线程将不知道锁并尝试读取/修改集合,而另一个线程在持有锁的情况下修改/读取(分别)

回答by Amir Ismail

Try to read a clone of your collection

尝试阅读您收藏的克隆

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

this will create a new copy of your collection that will not be affected by another thread but may cause a performance issue in case of huge collection.

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

So I think it will be better if you locked the collection while reading and writing processes.

所以我认为如果在读写过程中锁定集合会更好。

回答by sll

This is a pretty common mistake - modifying a collection whilst iterating it using foreach, keep in mind that foreachuses readonly IEnumeratorinstance.

这是一个非常常见的错误 - 在使用 迭代集合的同时修改集合foreach,请记住foreach使用只读IEnumerator实例。

Try to loop through the collection using for()with an extra index check so if the index is out of bounds you would be able to apply additional logic to handle it. You can also use LINQ's Count()as another loop exit condition by evaluating the Countvalue each time if the underlying enumeration does not implement ICollection:

尝试使用for()额外的索引检查循环遍历集合,因此如果索引超出范围,您将能够应用额外的逻辑来处理它。如果基础枚举未实现,您还可以Count()通过Count每次评估值来使用 LINQ作为另一个循环退出条件ICollection

If Markersimplements IColletion- lock on SyncRoot:

如果Markers实现IColletion- 锁定 SyncRoot:

lock (Markers.SyncRoot)

Use for():

使用for()

for (int index = 0; index < Markers.Count(); index++)
{
    if (Markers>= Markers.Count())
    {
       // TODO: handle this case to avoid run time exception
    }
}

You might find this post useful: How do foreach loops work in C#?

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

回答by Tim James

You can use a foreach but you have to cast the collection to a list and use the dot operator to access the behavior methods.

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

Example: Markers.Tolist().ForEach(i => i.DeleteObject())

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

Not totally sure what you're doing with your collection. My example is assuming you just wanted to delete all items from the collection, but it can be applied to any behavior you're trying to do with your collection.

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

回答by erionpc

I solved this problem by using

我通过使用解决了这个问题

var data = getData();
lock(data)
{
    return getData().Select(x => new DisplayValueModel(x));
}

instead of

代替

return getData().Select(x => new DisplayValueModel(x));