2012-03-29 72 views
8

我多线程应用程序,我得到这个错误集合被修改,枚举操作可能不会执行

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

任何想法解决这个问题?

+1

您的问题是用'foreach'里面的代码,请张贴。 – nemesv 2012-03-29 12:16:42

+3

你无法修改集合,而循环与foreach – Reniuz 2012-03-29 12:16:46

回答

4

您需要锁定阅读和写作两方面。否则,一个线程将不知道锁,并且将尝试读取/修改集合,而另一种是修改/与锁读(分别)举行

4

尝试阅读自己收藏的克隆

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

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

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

+0

...并修改原始集合。 – Reniuz 2012-03-29 12:24:36

+0

你是对的,我想使用'.Copy',但它可能会导致性能问题。 – 2012-03-29 12:28:26

8

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

尝试了通过使用for()有额外的索引检查收集循环,这样如果索引越界 - 你就可以申请额外的逻辑来处理这个问题,也可作为循环退出条件,你可以使用LINQ Count()这将评估计数值如果每一个潜在的枚举没有实现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#?

+0

但如果修改是从集合中删除一个项目,这将抛出一个'IndexOutOfRange'异常 – 2012-03-29 12:20:12

+1

我提到额外的索引检查以避免此问题,将添加示例,谢谢指向此 – sll 2012-03-29 12:22:26

+0

我想用foreach替换for但后来我想如果会锁定收藏更好,但不工作:/ – PATO7 2012-03-29 12:23:43

0

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

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

不能完全确定你和你的收藏做什么。我的例子假设你只是想删除集合中的所有项目,但它可以应用于你想要对你的集合进行的任何行为。

0

我会建议使用AsyncCommand,因为AsyncCommand要么被采取,要么使用lock(Markers)允许重入。(见https://github.com/StephenCleary/AsyncEx/wiki/AsyncLock):

private readonly AsyncLock _markersMutex = new AsyncLock(); 

    using (await _markersMutex.LockAsync().ConfigureAwait(false)) 
    { 
    foreach (GMapMarker m in Markers) 
    { 
     ... 
    } 
    } 

此外,AsyncLock让您更换Thread.Sleep其异步等同,await Task.Delay(TimeSpan.FromSeconds(1))

相关问题