2012-03-29 12 views
8

Tengo aplicación multihilos y me sale este errorcolección se modificó, la operación de enumeración no podrá ejecutar

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

Probablemente tengo un problema con mi colección, ya que por un hilo que había leído mi colección y el otro hilo modifico colección.

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


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

Estoy tratando de bloquear la recopilación con este código, pero no funciona.

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

¿Alguna idea para solucionar ese problema?

+1

Se problema es con el código dentro del 'foreach', por favor, publicarlo. – nemesv

+3

no puedes modificar la colección mientras bucles con foreach – Reniuz

Respuesta

4

Debe bloquear tanto el lado de lectura como el de escritura. De lo contrario, uno de los hilos no tendrá conocimiento de la cerradura y tratarán de leer/modificar la colección, mientras que el otro es la modificación/lectura (respectivamente) con el bloqueo mantenido

4

intenta leer un clon de su colección

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

esto creará una nueva copia de su colección que no se verá afectada por otro subproceso, pero puede causar un problema de rendimiento en caso de gran recopilación.

Así que creo que será mejor si bloqueaste la colección durante los procesos de lectura y escritura.

+0

... y modificar la colección original. – Reniuz

+0

tienes razón, creo que usar '.Copy' pero puede causar un problema de rendimiento. –

8

Esto es bastante común error - la modificación de una colección, mientras que la iteración usando foreach, tenga en cuenta que foreach utiliza sólo lectura IEnumerator ejemplo.

Pruebe el ciclo a través de la colección usando for() con índice adicional, así que si el índice está fuera de límite, podría aplicar lógica adicional para manejar esto, también como condición de salida de bucle puede usar LINQ Count() que evaluaría el valor de conteo cada vez que si la enumeración subyacente no implementa ICollection:

Si Markers implementos IColletion - bloqueo en SyncRoot:

lock (Markers.SyncRoot) 

uso for():

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

puede encontrar este post útil: How do foreach loops work in C#?

+0

pero si la modificación fue mediante la eliminación de un elemento de la colección, arrojará una excepción 'IndexOutOfRange' –

+1

Mencioné una verificación de índice adicional para evitar este problema, agregará una muestra, gracias por señalar – sll

+0

que pienso reemplazar foreach por pero entonces pensé que sería mejor bloquear la colección pero no funciona:/ – PATO7

0

Se puede utilizar un foreach, pero hay que echar la colección a una lista y utilizar el operador de punto para acceder a los métodos de comportamiento.

Ejemplo:. Markers.Tolist() ParaCada (i => i.DeleteObject())

No del todo seguro de lo que está haciendo con su colección. Mi ejemplo es asumir que solo quería eliminar todos los elementos de la colección, pero se puede aplicar a cualquier comportamiento que esté tratando de hacer con su colección.

0

Sugeriría usar AsyncCommand, porque AsyncCommand está tomado o no, mientras que el uso de lock(Markers) permite la reentrada.(Ver https://github.com/StephenCleary/AsyncEx/wiki/AsyncLock):

private readonly AsyncLock _markersMutex = new AsyncLock(); 

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

Además, AsyncLock permite reemplazar Thread.Sleep con su equivalente asíncrono, await Task.Delay(TimeSpan.FromSeconds(1))

Cuestiones relacionadas