with visual threadstart threads thread new multi method example espaƱol book c# multithreading

visual - threadstart c#



Threadsafe foreach enumeraciĆ³n de listas (9)

Necesito enumerar aunque IList genérico <> de objetos. El contenido de la lista puede cambiar, al ser agregado o eliminado por otros subprocesos, y esto matará mi enumeración con una "Colección modificada, la operación de enumeración puede no ejecutarse".

¿Cuál es una buena forma de hacer foreach sin hilos en un IList <>? Preferiblemente sin clonar la lista completa. No es posible clonar los objetos reales a los que hace referencia la lista.


Descubrirá que es un tema muy interesante.

El mejor enfoque se basa en ReadWriteResourceLock, que solía tener grandes problemas de rendimiento debido al llamado Problema de Convoy.

El mejor artículo que he encontrado tratando el tema es este de Jeffrey Richter, que expone su propio método para una solución de alto rendimiento.


El comportamiento predeterminado para una estructura de datos indexados simple como una lista vinculada, b-tree o tabla hash es enumerar en orden desde el primero hasta el último. No causaría un problema insertar un elemento en la estructura de datos después de que el iterador ya hubiera pasado ese punto o insertar uno que el iterador enumeraría una vez que hubiera llegado, y tal evento podría ser detectado por la aplicación y manejado si la aplicación lo requería. Para detectar un cambio en la colección y lanzar un error durante la enumeración, solo podía imaginar la (mala) idea de alguien de hacer lo que pensaban que el programador querría. De hecho, Microsoft ha arreglado sus colecciones para que funcionen correctamente. Han llamado a sus nuevas colecciones intactas brillantes ConcurrentCollections (System.Collections.Concurrent) en .NET 4.0.


Envuelva la lista en un objeto de bloqueo para leer y escribir. Incluso puede iterar con varios lectores a la vez si tiene un bloqueo adecuado, que permite múltiples lectores concurrentes, pero también un solo escritor (cuando no hay lectores).


Forech depende del hecho de que la colección no cambiará. Si desea iterar sobre una colección que puede cambiar, use la normal para construir y prepárese para un comportamiento no determinista. El bloqueo podría ser una mejor idea, dependiendo de lo que esté haciendo.


La clonación de la lista es la mejor y más fácil, ya que garantiza que su lista no cambiará por debajo de usted. Si la lista es demasiado grande para clonar, considere ponerle un candado que debe tomarse antes de leerla / escribir en ella.


No hay tal operación. Lo mejor que puedes hacer es

lock(collection){ foreach (object o in collection){ ... } }


Por lo tanto, los requisitos son: debe enumerar a través de un IList <> sin hacer una copia al mismo tiempo que agrega y elimina elementos.

¿Podría aclarar algunas cosas? ¿Las inserciones y eliminaciones ocurren solo al principio o al final de la lista? Si las modificaciones pueden ocurrir en cualquier punto de la lista, ¿cómo se comportará la enumeración cuando los elementos sean eliminados o agregados cerca o en la ubicación del elemento actual de la enumeración?

Esto es factible al crear un objeto IEnumerable personalizado con quizás un índice entero, pero solo si puede controlar todo el acceso a su objeto IList <> (para bloquear y mantener el estado de su enumeración). Pero la programación multiproceso es un asunto complicado en las mejores circunstancias, y esta es una probabilidad compleja.


Su problema es que una enumeración no permite que IList cambie. Esto significa que debe evitar esto mientras revisa la lista.

Algunas posibilidades vienen a la mente:

  • Clona la lista. Ahora cada enumerador tiene su propia copia para trabajar.
  • Serializar el acceso a la lista. Use un candado para asegurarse de que ningún otro hilo pueda modificarlo mientras se enumera.

Alternativamente, podría escribir su propia implementación de IList e IEnumerator que permita el tipo de acceso paralelo que necesita. Sin embargo, me temo que esto no será simple.


ICollection MyCollection; // Instantiate and populate the collection lock(MyCollection.SyncRoot) { // Some operation on the collection, which is now thread safe. }

Desde MSDN