Breve introducción a la programación en paralelo en .NET

Breve introducción a la programación en paralelo en .NET
Facebook Twitter Flipboard E-mail

Si bien la ley de Moore sigue aún activa, y tenemos la potencia de un superordenador de hace unos pocos años encima de nuestras rodillas, casi todos ya trabajamos en portátiles, también es cierto que el aumento de potencia cada vez es más debido a que nuestros procesadores tiene múltiples núcleos, que a la potencia en bruto de cada uno.

Por ello es imprescindible volver a hincar los codos y ponernos a aprender cosas como la multitarea, el multihilo o el multiproceso.

Y para dar una brevísima pincelada quiero traer un ejemplo desarrollado en C#, pero que es igual en cualquier lenguaje .NET, de diferentes formas de ir aumentando el rendimiento de nuestras aplicaciones.

Multihilo

Una de las formas de hacer paralelismo en por medio de ejecutar nuestra aplicación en diferentes y concurrentes hilos de ejecución. Los llamados Tread.

Para que sea más fácil de entender voy a construir un ejemplo muy sencillo en donde, en un aplicación de consola en C#, voy a imprimir en pantalla mil letras X y mil letras Y. Y que sería algo como así:

using System;
using System.Threading;

namespace GenbetaDevConsolePararell
{
    class Program
    {
        static void Main()
        {
            escribeUnaLetraX();
            escribeUnaLetraY();
            Console.ReadKey();
        }

        static void escribeUnaLetraX()
        {
            escribeUnaLetra('X');
        }
        static void escribeUnaLetraY()
        {
            escribeUnaLetra('Y');        
        }

        static void escribeUnaLetra(Char letra)
        {
            for (int i = 0; i < 1000; i++) Console.Write(letra);
        }
    }
}

Ya sé que es una forma muy rara de hacer el código, pero es que está pensado para poder invocar al mismo método desde diferentes hilos y poder pasarle información.

Programacion en Paralelo

Al lanzar obtengo una colección larga de letras X seguida de otra igualmente larga de letras Y. Asique ahora voy a ganar complejidad y voy a añadirle un hilo, un Tread, para que mientras me escribe una letra, también me escriba la otra.


        static void Main()
        {
            Thread t = new Thread(escribeUnaLetraY);     
            t.Start();
            escribeUnaLetraX();
            Console.ReadKey();
        }

Cuando lanzo la aplicación veo que el resultado de las letras está mezclado. Lo que señala que los hilos se pegan entre ellos para escribir y, en un escenario más complejo, hubiéramos aumentado el rendimiento al ejecutar el método dos veces al mismo tiempo. Y sin esperar que acabara el primero antes de lanzar el segundo.

Segundo resultado

Nota: en este ejemplo al ser un pintado en consola, no hay forma de visualizar la multitarea. Pero es preferible no ganar complejidad en la visualización para así tener el código más simple posible. Total, el objetivo del artículo es despertar la curiosidad del lector.

Paralell

Otra forma muy sencilla de aplicar paralelismo a nuestro código es por medio del espacio de nombre System.Threading.Tasks de .NET. El cual nos permite utilizar dos interesantes métodos que son Paralell.For y Paralell.ForEach.

Ambos lo que hacen es que cada una de las iteraciones del bucle es lanzada en su propio hilo de forma automática. Sin necesidad de que defina la creación, inicio y final por cada uno.

En nuestro ejemplo lo único que tendríamos que modificar sería el método que pinta en la pantalla. Específicamente el For,


        static void escribeUnaLetra(Char letra)
        {
             Parallel.For(0, 1000, i =>
             {
                 Console.Write(letra);
                 i++;
             });
        }

Como ves la construcción es ligeramente diferente a un For normal. Lo más llamativo es que todo lo ocurre dentro del bucle se describe como una expresión lambda de i.

En este ejemplo no se nota el aumento del rendimiento ya que finalmente la consola se ha convertido en el cuello de botella del código. Pero en un proyecto recientemente finalizado en donde debía parsear más de dos millones de registros, la velocidad pasó de 4 registros por segundo con cuatro hilos a más de 1000 registros por segundo con un Parallel.ForEach en el parseador y otro en el almacenamiento en la base de datos.

Pero ojo, no todo es perfecto. Y puedes tener problemas de bloqueos y de duplicidades. Hay que analizarlo un poquito más antes de lanzar este tipo de ejecuciones paralelas porque, a menos que lo definas específicamente, el orden es cuasi aleatorio (realmente no, pero eso sobrepasa el contenido de este artículo) y pueden llegar los resultados antes de lo esperado.

Espero haberte causado curiosidad sobre la programación paralela (que no para lelos) y te aviso que en .NET 4.5, que ya estoy toqueteando en mi Visual Studio 11 Developer Preview, es aún más sencillo.

Más información | Threading in C# by Joseph Albahari, Parallel.For Method en MSDN En GenbetaDev | Serie de Introducción al multiprocesamiento en C++

Comentarios cerrados
Inicio