• Inicio
  • Acerca de
  • Contacto

Destino DotNet

Blog de ayuda y recursos sobre tecnologías .NET

Nov 7, 2014

Programación asíncrona 1: Delegados asíncronos

Programación Asíncrona 1 - Delegados
Facebook0
Twitter0
Google+0
Linkedin0
E-Mail0

La programación asíncrona es un esquema de programación que puede mejorar mucho el rendimiento de tus desarrollos, si lo aplicas correctamente.

Por ejemplo, a veces nuestros aplicativos se quedan congelados cuando el usuario interactúa con ellos. Esto ocurre porque hay algunas tareas pesadas que se están ejecutando, y como sólo hay un hilo de ejecución, no le queda otra que esperar a que termine dicha tarea para estar nuevamente disponible. Mejorar este tipo de comportamientos puede darle un toque de profesionalidad a tus aplicativos que siempre es importante.

Hay varias formas de implementar la programación asíncrona. El día de hoy veremos la primera: Delegados asíncronos.

¿Qué es un Delegado?

Un delegado es la implementación de .NET de los punteros a funciones. Un delegado es un tipo de dato, por lo que se puede instanciar, y a través de estas variables llamar a funciones que tienen la misma estructura (tipo de datos de retorno y parámetros) que el delegado.

Vamos con un ejemplo (muy) sencillo. Declaramos un delegado:

    delegate int delegadoProducto(int a, int b);

Luego creamos una función con la misma estructura del delegado (ojo que la creamos en una clase separada):

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

Ahora instanciamos un objeto de nuestro delegado, asignándole la función que creamos en el paso anterior; y lo ejecutamos.

        public static void Main(string[] args)
        {
            // Instanciamos un delegado
            var objDelegado = new DelegadoSimple();

            // 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(2, 3);

            // Imprimimos el resultado de la ejecución
            Console.WriteLine("Resultado del producto es: {0}", resultado.ToString());
            Console.ReadLine();
        }

Como ven, sólo se puede asignar funciones a objetos que tienen la misma estructura que su delegado.

La salida de la ejecución es la siguiente:

Resultado de ejecución, llamada a delegado

Podría parecer que este esquema es innecesario, ya que se puede llamar directamente a la función sin necesidad de usar un delegado, pero recuerda que es un ejemplo simple para fijar conceptos. Los delegados son muy usados para la gestión de eventos, llamadas asíncronas, accesos más permisivos en diferentes hilos de ejecución e incluso métodos anónimos. Puedes encontrar más información de delegados en este enlace o en este otro enlace.

Llamada síncrona a un delegado

Ahora que ya vimos cómo crear un delegado y cómo usarlo, vamos con un ejemplo un poco más elaborado para entender mejor el concepto de llamada síncrona.

Para este ejemplo vamos a usar a nuestros amigos jedis Anakin y Obi Wan. Ellos harán un viaje interplanetario que les demora 5 segundos aproximadamente. Es lo bueno de la velocidad de la luz.

Tenemos el siguiente código:

using System;
using System.Diagnostics;

namespace ProgramacionAsincrona.Delegados
{
    class Program
    {
        delegate int DelegadoViajeInterplanetario(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.
            DelegadoViajeInterplanetario viajeAnakin = new DelegadoViajeInterplanetario(objViaje.ViajeInterplanetario);

            // Se crea el segundo delegado, que representará el segundo viaje.
            DelegadoViajeInterplanetario viajeObiWan = new DelegadoViajeInterplanetario(objViaje.ViajeInterplanetario);

            // Ahora ejecutamos los delegados que harán los viajes
            viajeAnakin("Anakin", temporizador);
            viajeObiWan("ObiWan", temporizador);

            // Hacemos una pequeña pausa y luego mostramos el mensaje de fin de viajes
            System.Threading.Thread.Sleep(500);
            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 ViajeInterplanetario(string NombreViajero, Stopwatch temporizador)
        {
            Console.WriteLine();
            Console.WriteLine("Inicio de viaje realizado por {0}.", NombreViajero);
            System.Threading.Thread.Sleep(5000);
            Console.WriteLine("Fin de viaje realizado por {0}, a los {1} segundos.", NombreViajero, temporizador.Elapsed.TotalSeconds.ToString());
            return 0;
        }
    }

}

Lo que hacemos en el código es crear la función ViajeInterplanetario que representará los viajes que harán los personajes.

Además, se crean 2 delegados que suscriben esta función y se llaman de forma síncrona (líneas 24 y 25):

            // Ahora ejecutamos los delegados que harán los viajes  (llamadas síncronas)
            viajeAnakin("Anakin", temporizador);
            viajeObiWan("ObiWan", temporizador);

También tenemos un objeto de apoyo llamado temporizador, que nos sirve para calcular el tiempo que demoran las llamadas y el programa principal.

Si ejecutamos el proyecto tenemos la siguiente salida:

Resultado de ejecución, llamada síncrona a delegado

Puedes notar que la ejecución 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 10 segundos.

Llamada asíncrona a un delegado

Para probar el siguiente concepto haremos un cambio en la llamada a los delegados para que se ejecuten de forma asíncrona. 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)
            viajeAnakin.BeginInvoke("Anakin", temporizador, null, null);
            viajeObiWan.BeginInvoke("ObiWan", temporizador, null, null);

Si ahora ejecutamos el programa, tendremos lo siguiente:

Resultado de ejecución, llamada asíncrona a delegado

Como puedes ver ahora la salida de la ejecución es diferente. 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.

Primero se muestran los mensajes de las llamadas a los viajes, luego se muestra el mensaje de fin de llamadas, para finalmente mostrar el mensaje de fin de viaje de cada llamada, que se ejecutaron de forma asíncrona.

Esto hace que al final el tiempo de ejecución de este programa, con la variante de las llamadas asíncronas, sea de aproximadamente 5 segundos, que es prácticamente la mitad del tiempo comparándolo con el tiempo de ejecución con llamadas síncronas del ejemplo anterior. Esto se explica porque los viajes se ejecutaron en «paralelo» (lo pongo en comillas porque hay toda una teoría sobre los procesos en paralelo, que espero abordarlo en un artículo posterior).

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. Pero no siempre es así: funciona bien para tareas que implican muchos cálculos (uso intensivo del procesador), pero para el manejo de archivos (por ejemplo) las ejecuciones asíncronas podrían demorar igual o más que si ejecutáramos las tareas de forma secuencial.

Como reflexión final quiero decir que si bien es cierto que para entender el concepto de programación asíncrona se ha hablado bastante de hilos de ejecución, hay que tener en claro que no son lo mismo. Los hilos son una herramienta que nos sirve para implementar la asincronía en nuestros aplicativos.

Espero que este artículo sea útil para entender la programación asíncrona. En los siguientes artículos de esta serie explicaré otras formas que existen para su implementación, tratando de seguir de forma cronológica la evolución de este tema en las versiones del .NET Framework.

Facebook0
Twitter0
Google+0
Linkedin0
E-Mail0

Relacionado

Berto Martinez
Berto Martinez

Ingeniero de Software, desarrollador a tiempo completo y entusiasta de las tecnologías de la Web 2.0 a tiempo incompleto. También soy autor de este blog, donde comparto lo aprendido en estos años sobre tecnologías .NET, esperando que sea últil para los visitantes.

Article by Berto Martinez / Tutoriales / .NET, .NET Framework, C#, Delegados 2 comentarios

Suscríbete al Blog

Recibirás todos los contenidos en tu correo electrónico:

Categorías

  • General
  • Opinión
  • Recursos
  • Tutoriales

Lo que Google obliga a hacer

Síguenos en Twitter

Mis tuits
Privacy & Cookies: This site uses cookies. By continuing to use this website, you agree to their use.
To find out more, including how to control cookies, see here: Política de cookies
Licencia Creative Commons
Destino DotNet y su contenido es creado por Joel Martinez y se distribuyen bajo una Licencia Creative Commons Atribución-CompartirIgual 4.0 Internacional.

Copyright © 2025 · Education Pro Theme en Genesis Framework · WordPress · Iniciar sesión