Programación Asíncrona | Trabajando con Delegados Asincronos C#

Se ha venido hablando mucho en los últimos años sobre la programación Asíncrona, ya que este esquema de programación puede mejorar mucho nuestros programas claro está si lo aplicamos correctamente.

En ocasiones nuestros programas nos dan la notificación de que el programa no responde o se congela  cuando el usuario interactúa con ellos. Esto ocurre porque hay algunas tareas pesadas que se están ejecutando detrás, y como sólo hay un hilo de ejecución,  entonces no le queda otra opción  que esperar a que termine dicha tarea para estar nuevamente disponible. Mejorar este tipo de comportamientos les da un toque de profesionalidad a nuestro trabajo algo  que siempre es importante.

En esta entrada veremos cómo aplicar la programación asíncrona usando delegados asíncronos, en otra ocasión hace tiempo escribí una entra sobre los delegados la puede leer en el siguiente enlace aquí y otro aquí.

Si con los enlaces no basta para comprender, a continuación haremos un pequeño ejemplo de cómo funcionan los delegados.

¿Qué es un Delegado?

Un delegado es la implementación de .NET de los punteros a funciones.

Ahora vamos al ejemplo “suuuuper” sencillo de un delegado.

Primero declaramos un delegado:

  delegate int delegadoProducto(int a, int b);

A continuación creamos una función con la misma estructura del delegado:

public class DelegadoProducto
    {
        public int Producto(int x, int y)
        {
            Console.WriteLine();
            Console.WriteLine("DelegadoProducto.Producto: Calculando el producto de {0} por {1}", x.ToString(), y.ToString());
            Console.WriteLine();
            return x * y;
        }
    }

Ahora instanciamos un objeto de nuestro delegado:

  public static void Main(string[] args)
        {
            // Instanciamos un delegado
            var objDelegado = new DelegadoProducto();
 
            // Asignamos el objeto recién creado a la función que creamos en la clase de apoyo
            delegadoProducto variableDelegadoProducto = new delegadoProducto(objDelegado.Producto);
 
            // Llamamos a la función a través del delegado
            var resultado = variableDelegadoProducto(4, 4);
 
            // Imprimimos el resultado de la ejecución
            Console.WriteLine("Resultado del producto es: {0}", resultado.ToString());
            Console.ReadLine();
        }

Si se fijan Sólo se puede asignar funciones a objetos que tienen la misma estructura que su delegado.

Esta es la salida de nuestro programa en consola en mi caso terminal puesto que estoy usando Mac OS.

Aplicando la programación Asíncrona a nuestros delegados.

Vamos con un ejemplo un poco más elaborado para entender mejor el concepto de llamada síncrona.

Para este ejemplo voy a usar a dos de mis amigas  Naiomi y Maria. Ellas harán un viaje hacia la ciudad de NEW YORK que demora 15 segundos aproximadamente. ( es solo un ejemplo)

El codigo el siguiente:

sing System;
using System.Diagnostics;
 
namespace ProgramacionAsincrona.Delegados
{
    class Program
    {
        delegate int DelegadoViajeNYC(string NombreViajero, Stopwatch temporizador);
 
        static void Main(string[] args)
        {
            // Se instancia un temporizador para calcular el tiempo de ejecución.
            var temporizador = Stopwatch.StartNew();
            
            // Se instancia la clase de apoyo
            var objViaje = new Viaje();
 
            // Se crea el primer delegado, que representará el primer viaje.
            DelegadoViajeNYC viajeNaiomi = new DelegadoViajeNYC(objViaje.ViajeNYC);
 
            // Se crea el segundo delegado, que representará el segundo viaje.
            DelegadoViajeNYC viajeMaria = new DelegadoViajeNYC(objViaje.ViajeNYC);
 
            // Ahora ejecutamos los delegados que harán los viajes
            viajeNaiomi("Naiomi", temporizador);
            viajeMaria("Maria", temporizador);
 
            // Hacemos una pequeña pausa y luego mostramos el mensaje de fin de viajes
            System.Threading.Thread.Sleep(1500);
            Console.WriteLine();
            Console.WriteLine("=> Finalizó las llamadas a los viajes, a los {0} segundos.", temporizador.Elapsed.TotalSeconds.ToString());
            Console.WriteLine();
 
            Console.ReadLine();
        }
    }
 
    public class Viaje
    {
        public int ViajeNYC(string NombreViajera, Stopwatch temporizador)
        {
            Console.WriteLine();
            Console.WriteLine("Inicio de viaje realizado por {0}.", NombreViajera);
            System.Threading.Thread.Sleep(15000);
            Console.WriteLine("Fin de viaje realizado por {0}, a los {1} segundos.", NombreViajera, temporizador.Elapsed.TotalSeconds.ToString());
            return 0;
        }
    }
 
}

Recapitulando

Si eres observador puedes notar que la ejecución del programa es lineal: se llama al primer viaje, cuando éste termina se llama al segundo viaje y cuando éste segundo termina se muestra el mensaje de fin de llamadas de viajes. Esto sucede porque estamos llamando a los delegados de forma síncrona, que es casi lo mismo que llamar directamente a la función. El tiempo total de ejecución de todo el programa es un poco más de 31 segundos.

Realizando llamada asíncrona a un delegado

Cambiamos las líneas 24 y 25 por llamadas asíncronas a los delegados.

            // Ahora ejecutamos los delegados que harán los viajes  (llamadas asíncronas)
            viajeNaiomi.BeginInvoke("Naiomi", temporizador, null, null);
            viajeMaria.BeginInvoke("Maria", temporizador, null, null);

Si ejecutamos nuestro codigo el resultado seria el siguiente:

El programa principal hace las llamadas (asíncronas) a los delegados y sigue su ejecución sin esperar a que estas llamadas terminen. Internamente la ejecución de dichas llamadas sucede en un hilo diferente al hilo del programa principal. Este manejo de hilos es interno, de forma transparente para nosotros.

Conclusiones

Ya puedes imaginar el potencial de esta técnica, cómo implementar este tipo de llamadas para algunos procesos que demanden muchos recursos y poder reducir los tiempos de ejecución dividiendo las tareas a través de llamadas asíncronas. Espero que la apliquen en sus próximos desarrollos.

 

 

 

 

Ejemplo delegado C#

El siguiente ejemplo muestra el funcionamiento de un delegado,  que la primera instancia se le suma la cantidad indicada en el delegado can1 y se multiplica lo indicado en el delegado can2.

using System;

delegate int NumberChanger(int n);
namespace Delegateejemplo
{
 class blogmatias
 {
 static int num = 3;
 public static int AddNum(int l)
 {
 num += l;
 return num;
 }

 public static int MultNum(int f)
 {
 num *= f;
 return num;
 }
 public static int getNum()
 {
 return num;
 }

 static void Main(string[] args)
 {
 //creando los objetos delegates
 NumberChanger can1 = new NumberChanger(AddNum);
 NumberChanger can2 = new NumberChanger(MultNum);

 //llamando los metodos usando los delegate 
 can1(10);
 Console.WriteLine("Valor numero 1: {0}", getNum());
 can2(2);
 Console.WriteLine("Valor numero 2: {0}", getNum());
 Console.ReadKey();
 }
 }
}

Cualquier duda o comentario no dudes en dejarnos saber 🙂 !!!

Cuándo y Porqué usar delegados C# ?

etSq-1

Cuándo y Porqué usar delegados C# ?

Si somos relativamente nuevos usando C# o en el mundo de la programación de seguro nos habremos preguntado Cuándo y Porqué usar delegados ? pues en esta entrada daremos respuesta a dicha interrogante.

Que es un delegado ?

Un delegado es un tipo que representa referencias a métodos con una lista de parámetros determinada y un tipo de valor devuelto.

Este puede ser visto como un marcador de posición para un / algunos métodos.

Características de los delegados?

  •  Son similares a los punteros de C ++ de función, pero son de tipo seguros.
  •  Permiten métodos que se pasan como parámetros.
  •  Pueden ser utilizados para definir callback methods (devolución de llamada).
  •  Se pueden encadenar; por ejemplo, varios métodos pueden ser
    llamada en un solo evento.

Cuando usar un delegado?

Los delegados son útiles porque nos ofrecen  la capacidad de personalizar el comportamiento de  los objetos. La mayoría de las veces, podemos  utilizar otras formas para lograr el mismo propósito y no creo que uno puede ser forzado a crear delegados. Es simplemente la manera más fácil en algunas ocasiones para personalizar el comportamiento de los objetos.

nota: No es necesario el uso de los delegados con los parámetros; podemos utilizar delegados con los parámetros y sin parámetros.

Porque usar delegados ?

Cuando trabajamos con delegados, la cantidad de código a refactorizar se reducirá significativamente, ya que en vez de tener un gran número de métodos rígidos cada uno haciendo una tarea muy específica, que tendrá un menor número de métodos mucho más generales que pueden ser mejoradas para manejar una amplia variedad de escenarios posibles. Nuestro código será ampliamente reutilizable.

Los delegados se prestan altamente a producir código altamente reutilizable y seco. Son muy eficaces cuando se usan como devoluciones de llamada (callback) a distintos eventos del sistema, manejo de estructuras de datos que tendrán código común que afectan a sus diversas partes, o si deseamos escribir código en un estilo más funcional, tales como la utilización de técnicas de programación funcionales avanzadas Currying

Hasta aquí la entrada si quieres agregar algún punto a los tópicos ya tratados no dejes de comentar !!

Happy Coding 🙂