Mejorando el performance de tu app, trabajar con ConcurrentBag

Una aplicación puede tener un performance o rendimiento que no sean los óptimos por muchos motivos, pero uno de ellos puede ser el hecho de no trabajar bien con procesos que podrían paralelizarse y no se paralelizan.

En caso de paralelizar procesos además hay que tener en cuenta que tu aplicación va a trabajar con diferentes hilos, y que no todos los elementos de C# son seguros al ser manejados por diferentes hilos paralelos.

A continuación presento por qué usar ConcurrentBag, un elemento que implementa IEnumerable como pueda ser List, y que deberá sustituir a List si las vas a necesitar en un proceso con hilos paralelos que tienen subprocesos.

En una anterior entrada presento como mejora de performance el hecho de trabajar con Parallel para paralelizar procesos.

Imagina que tienes un código como el siguiente (muy simplificado para el ejemplo):

De este modo tienes una lista con los elementos 1, 2, 3, y quieres aumentar la lista añadiendo 4, 5 y 6, pero de forma paralela en lugar de secuencial.  4, 5, 6 se añadirán en el mismo instante de tiempo, si cada operación tardase x ms, el tiempo total será x ms y no 3x ms.

En este caso es una operación muy simple para ilustrar el ejemplo, pero imagina que tienes una llamada http a un servicio que recupera datos dadas unas primary keys en una lista, y luego quieres añadir los datos obtenidos en una lista, ya no es algo tan simple, y una llamada http puede tardar del orden de centenas de ms siendo optimistas, segundos incluso (con total normalidad). Por lo que te interesa no multiplicar esos tiempos, si no que tu proceso dure lo que el peor de los subprocesos.

¿Y dónde está el problema?

Una lista no es un objeto thread safe, se puede leer en el momento que se quiera cuantas veces se quiera, pero tratar de escribir mientras esta siendo leida puede producir un error, un error que provocará que el contenido escrito no se ajuste al contenido que se deseaba escribir, o devolver una excepción según el código que incluyas en el proceso paralelo, u otro tipo de errores silenciosos que podrías pasar por alto.

¿Cómo lo arreglas de manera sencilla sin penalizar performance?

Pues cambiando el tipo de dato List, por ConcurrentBag. Este objeto no producirá esos problemas, y al terminar el proceso paralelo siempre podrás convertirlo a lista con un ToList();.

Un consejo muy práctico para evitar errores si usar paralelos en procesos que lo permitan para mejorar tu performance.

Un saludo.

Deja un comentario