Desde .NET 4, los hilos en C# se convirtieron en una tarea de lo más fácil. En este post vamos a realizar una par de muestras, una prueba sencilla donde vamos a ejecutar una tarea que usa mucho tiempo en terminarse como es la obtención de π(x) o la cantidad de números primos menores que un número.Vamos a usar una función lambda para obtener el codiciado π(x) y para ello vamos a usar la clase Enumerable del espacio System.Linq y un método llamado Range para obtener un conjunto de números enteros entre dos rangos numerable.Range(2, max), contamos los elementos con el predicado con el que obtengamos todos los números entre el rango de 2 y la raiz del número Count(n => Enumerable.Range(2, (int)Math.Sqrt(n) - 1) que cumplan la condición de tener un módulo mayor que 0 All(i => n % i > 0))o lo que es lo mismo todos los números que no son múltiplos.

Enumerable.Range(2, max).Count(n => Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0))

Linq es bestial, ¿no?
Si le damos un valor alto a la variable max com por ejemplo 10.000.000, la tarea dejará la interfaz bloqueada y por tanto tendremos que crear un hilo para evitar este problema y aquí viene lo bueno de la clase Task.

Esta clase tiene un método Factory.StartNew al que se le pasa un método como delegado, el cual se ejecutará de forma asincrónica devolviendo la tarea iniciada, es decir no devuelve un valor sino la tarea Task en si con sus propiedades y métodos.

Task.Factory.StartNew(() => TareaMuyCostosa());
En nuestro código vamos a ver solo dos aspectos de los muchos de los que podemos extraerle a Task, uno es la propiedad IsCompleted y otra sería el método Wait.
Nuestra aplicación como he dicho, generará números primos en una tarea y mientras que no esté finalizada, mostrará un mensaje indicando el tiempo empleado y una vez que finalice, mostrará el resultado que lo mostraremos de dos modos, uno esperando a que finalice y otro con el método ContinueWith.

La primera

static void primeNumbersCount(int max)
        {
            Stopwatch stw = new Stopwatch();
            stw.Start();
            int sec = 0;
            var piXTask = Task.Factory.StartNew(() => Enumerable.Range(2, max).Count(n => Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0)));

            var calculando = "Calculando ";
            while (!piXTask.IsCompleted || piXTask.IsFaulted || piXTask.IsCanceled)
            {
                Thread.Sleep(1000);
                Console.Clear();
                Console.WriteLine(calculando.PadRight(sec % 5 + 11,'.'));
                Console.WriteLine(string.Format("Tiempo empleado: {0} sengundos", sec++));
            }

            piXTask.Wait();

            string text = string.Format("Calculo de {0} números primos menores de {1}", piXTask.Result, max);
            Console.WriteLine(text);
            Console.WriteLine(string.Format("Tiempo empleado: {0}", stw.Elapsed));
            Console.Read();
        }

En este código, vemos que se lanza la tarea, luego pasamos a un bucle del que debemos asegurarnos que saldremos algún día (esta opción no la aconsejo, pero para mostrar que la app se queda en sus cosas mientras la tarea se está realizando, está bien), mediante la propiedad IsCompleted, IsFaulted o IsCanceled, es decir cuando pase alguna de estas cosas, sale del bucle.
Luego pasamos al método Wait, que lo que hace es lo mismo que el bucle, hasta que la tarea no haya finalizado, espera y no sobrepasa a la siguiente línea de código. Una vez acabado, muestra el resultado con la propiedad Result del tipo int, porque es el resultado que entrega el delegado que lanzó. Y el resultadotask03task01

Pero para mi, en este caso usaría lo mismo pero con el método ContinueWith.

static void primeNumbersCount(int max)
        {
            Stopwatch stw = new Stopwatch();
            stw.Start();
            int sec = 0;
            var piXTask = Task.Factory.StartNew(() => Enumerable.Range(2, max).Count(n => Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0)));
            var calculando = "calculando ";
            piXTask.ContinueWith((p) =>
            {
                string text = string.Format("Calculo de {0} números primos menores de {1}", piXTask.Result, max);
                Console.WriteLine(text); Console.WriteLine(string.Format("Tiempo empleado: {0}", stw.Elapsed));
            });
            Console.WriteLine("Esto no ha terminado...");
            Thread.Sleep(1000); sec++;
            while (!piXTask.IsCompleted)
            {
                Thread.Sleep(1000); Console.Clear();
                Console.WriteLine(calculando.PadRight(sec % 5 + 11, '.'));
                Console.WriteLine(string.Format("Tiempo empleado: {0} segundos", sec++));
            }
          }

Aquí podemos apreciar que se lanza la tarea, vemos el método ContinueWith que contiene código en su interior mediante otro delegado y posteriormente el código ese que hemos creado para mostrar cosas mientras la tarea se realiza. Al ejecutarlo, se lanza la tarea y se obvia el código del método ContinueWith en cuestión, continua y pasa al bucle, una vez que ha terminado la tarea, continua con lo que le queda dentro del bloque ContinueWith y se acabó, ¿fácil? facilísimo!!

Video4
Ya veremos en otra ocasión el uso de Arrays de Task, esperas, sincronías entre ellos y alguna otra cosa más.
Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s