Коллекция была изменена, операция перечисления может не выполняться - PullRequest
7 голосов
/ 29 марта 2012

У меня есть приложение с многопоточностью, и я получаю эту ошибку

************** 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 ]

11 голосов
/ 29 марта 2012

Это довольно распространенная ошибка - изменение коллекции при ее повторении с использованием foreach, имейте в виду, что foreach использует только для чтения IEnumerator экземпляр.

Попробуйте перебрать коллекцию, используя for() с дополнительной проверкой индекса, поэтому, если индекс выходит за пределы, вы сможете применить дополнительную логику для его обработки. Вы также можете использовать LINQ Count() в качестве другого условия выхода из цикла, каждый раз оценивая значение 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
    }
}

Возможно, этот пост окажется полезным: Как работают циклы foreach в C #?

4 голосов
/ 29 марта 2012

Попробуйте прочитать клон вашей коллекции

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

это создаст новую копию вашей коллекции, которая не будет затронута другим потоком, но может вызвать проблему производительности в случае огромной коллекции.

Так что я думаю, будет лучше, если вы заблокируете коллекцию во время чтения и записи.

4 голосов
/ 29 марта 2012

Вам нужно заблокировать как на стороне чтения и записи.В противном случае один из потоков не будет знать о блокировке и попытается прочитать / изменить коллекцию, в то время как другой изменяет / читает (соответственно) с удерживаемой блокировкой

0 голосов
/ 22 июня 2018

Я решил эту проблему с помощью

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

вместо

return getData().Select(x => new DisplayValueModel(x));
0 голосов
/ 18 октября 2017

Вы можете использовать foreach, но вы должны привести коллекцию к списку и использовать оператор точки для доступа к методам поведения.

Пример: Markers.Tolist (). ForEach (i => i.DeleteObject ())

Не совсем уверен, что вы делаете со своей коллекцией. Мой пример предполагает, что вы просто хотите удалить все элементы из коллекции, но это может быть применено к любому поведению, которое вы пытаетесь сделать со своей коллекцией.

...