Utilizar un mismo objeto desde varios hilos con la instrucción lock c#
Hola, quienes hayan tenido la posibilidad de trabajar con múltiples hilos (Threads) se pudo haber enfrentado a con la dificultad de trabajar con un objeto al que muchos hilos tienen acceso, posiblemente obteniendo como resultado el famosisimo Colección modificada; puede que no se ejecute la operacion de enumeración técnicamente un InvalidOperationException.
Por qué? en que momento sucede?
Pues bien, primero entendamos el motivo de esta excepción: Cuando tienes por ejemplo una lista estática en una clase que almacena unos productos específicos
public class Publicas { public static List<Producto> ProductosDestacados { get; set; } }
Y tienes dos subprocesos (hilos) diferentes en los que necesitas acceder y modificar esta lista:
Proceso 1 hace:
foreach(Producto p in Publicas.ProductosDestacados) { //Agregar elementos tipo Producto a un GridView }
Proceso 2 hace:
foreach(Producto p in Publicas.ProductosDestacados) { if(p.ProductoId == 10) Publicas.ProductosDestacados.Remove(p); }
Si el Proceso 1 estuviera en ejecución y el Proceso 2 elimina un elemento de la lista ProductosDestacados la colección o lista de elementos cambiaría provocando así que la sentencia foreach del Proceso 1 falle y salte la excepción.
Entonces cómo lo soluciono?
Utiliza for en vez de foreach: Utilizando una sentencia for para recorrer la lista te evitas el problema porque únicamente utilizas esta lista para obtener un elemento por su indice; el foreach no la libera hasta que termina.
Utiliza la instrucción lock La instrucción lock te permite garantizar que un subproceso no va a entrar en una sección crucial de código mientras otro subproceso ya se encuentre en ella. Genial no?
Y cómo utilizar la instrucción lock?
Veamos cómo se utiliza la instrucción lock en un sencillo ejemplo suponiendo el mismo caso anterior: Proceso 1 y Proceso 2:
Proceso 1 hace:
lock (Publicas.ProductosDestacados) { foreach(Producto p in Publicas.ProductosDestacados) { //Agregar elementos tipo Producto a un GridView } }
Proceso 2 hace:
lock (Publicas.ProductosDestacados) { foreach(Producto p in Publicas.ProductosDestacados) { if(p.ProductoId == 10) Publicas.ProductosDestacados.Remove(p); } }
Si no es suficientemente claro el ejemplo diría que si otro subproceso intenta entrar en un código bloqueado, esperará, o se bloqueará, hasta que el objeto se libere.
Riesgos al utilizar la sentencia lock
Aunque se ve muy bonito, practico y util, hay que tener mucho cuidado en excederse en el uso de la sentencia lock. En msdn dice claramente:
En general evite bloquear un tipo public o instancias que estén fuera del control de su código. Las construcciones comunes lock (this), lock (typeof (MyType)) y lock ("myLock") incumplen esta instrucción:
Mi recomendación es: No usar esta instrucción si el elemento bloqueado es utilizado por subprocesos cuyos bloqueos sean muy largos y a la vez el mismo elemento es utilizado por el mismo proceso de interfaz gráfica de la aplicación.
Espero sea de ayuda para alguien.