Llamadas asíncronas con C#

En este articulo vamos a explicar como realizar llamadas asíncronas  con C# – en realidad con cualquier lenguaje .NET. El mecanismos que nos proporciona el .NET Framework para estas tareas son los delegados. Si queremos una introducción a los delegados podemos ver este enlace. Delegados en C#

Para este ejemplo hemos utilizado una aplicación de consola de VS2008.

El objetivo de este ejemplo es mostrar como llamar asíncronamente a un método – normalmente debido a que este tarda mucho tiempo en finalizar. Nuestro método es el siguiente:

string ElMetodoAsincrono(string input)
{
    Console.WriteLine("Inicio de la ejecución del método \"ElMetodoAsincrono\"");
    Console.WriteLine("El parámetro es -{0}-", input);
    Thread.Sleep(3000);
    Console.WriteLine("Finalización de la ejecución del método \"ElMetodo\".");
    return "Valor de retorno";
}

Hemos forzado una pausa de tres segundos - Thread.Sleep(3000); - para que la ejecución del método tarde y poder ver más claramente la ejecución asíncrona.

Lo primero que necesitamos para poder realizar la ejecución de forma asíncrona es definir un delegado con la misma firma que nuestro método – de esta forma:

delegate string DType(string input);

El delegado nos va a proporcionar el método BeginInvoke que es el que nos va a permitir conseguir nuestro objetivo. Este método recibirá tantos parámetros como tenga definido el delegado (en nuestro caso 1) más dos especiales: el callback – la función que queramos que se ejecute al finalizar la llamada asíncrona y un segundo parámetro de tipo object que nos va a permitir enviar argumentos adicionales. Ambos parámetros admiten el valor null. Podemos acceder a la documentación de Microsoft desde este enlace - http://msdn.microsoft.com/es-es/library/a06c0dc2(v=VS.80).aspx

La llamada devuelve un objeto que implementa IAsyncResult que nos va a permitir a través de sus propiedades conocer el estado de la ejecución asíncrona en el hilo principal.

Ejemplo – que es como se entienden las cosas:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace LlamadasAsincronasSample
{
    class Program
    {
        delegate string DType(string input);

        static void Main(string[] args)
        {
            
            Console.WriteLine("Inicio de ejecución de código.");
            Console.WriteLine("Antes de la llamada asíncrona");
            Program p = new Program();
            DType method = new DType(p.ElMetodoAsincrono);

            IAsyncResult a = method.BeginInvoke("www.devjoker.com", null, null);

            Console.WriteLine("Después de la llamada asíncrona, Hilo principal");

            Console.ReadLine();

        }       

        string ElMetodoAsincrono(string input)
        {
            Console.WriteLine("Inicio de la ejecución del método \"ElMetodoAsincrono\"");
            Console.WriteLine("El parámetro es -{0}-", input);
            Thread.Sleep(3000);
            Console.WriteLine("Finalización de la ejecución del método \"ElMetodo\".");
            return "Valor de retorno";
        }
        
    }
    
}

En el ejemplo únicamente creamos una variable – identificada por method – del tipo delegado que hemos definido anteriormente, y llamamos al método BeginInvoke. En este caso no ejecutamos ningun callback – aunque mas adelante si lo haremos-.

Si observamos la ejecución de la consola veremos que el mensaje de finalización del método Main se muestra antes que el mensaje de finalización de la ejecución del método asíncrono.

asyn1

El siguiente paso es ver como realizar un callback, es decir, ejecutar “algo” – una acción - cuando finalice la ejecución de “ElMetodoAsincrono”. Modificamos la llamada e incluimos un delegado anónimo – (una función sin nombre!) – como segundo parámetro de la función.

IAsyncResult a = method.BeginInvoke("www.devjoker.com", delegate(IAsyncResult res)
{
    Console.WriteLine("Finalización de la ejecución asíncrona!");
}, null);

Sencillo, pero ¿como podemos obtener el valor de retorno que devuelve la ejecución del “ElMetodoAsincrono”?. Pues también sencillo, el delegado dispone del método EndInvoke – que retorna el mismo tipo que delegado. Ejemplo:

IAsyncResult a = method.BeginInvoke("www.devjoker.com", delegate(IAsyncResult res)
{
    string data = method.EndInvoke(res);
    Console.WriteLine("Esto es lo que tiene data {0}", data);
}, null);

Notad que la variable method está definida fuera del ámbito del delegado anónimo … sin embargo funciona … esto es una clausura.

Vamos a dejarlo chulo usando una expresión Lamda en lugar del delegado anónimo (solo a partir de la versión 3.5 del framework)

IAsyncResult a = method.BeginInvoke("www.devjoker.com", (res) =>
    {
        string data = method.EndInvoke(res);
        Console.WriteLine("Esto es lo que tiene data {0}", data);
    }
, null);

El resultando es el siguiente:

asyn2

El código completo del ejemplo:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace LlamadasAsincronasSample
{
    class Program
    {
        delegate string DType(string input);

        static void Main(string[] args)
        {
            
            Console.WriteLine("Inicio de ejecucion de código.");
            Console.WriteLine("Antes de la llamada asíncrona");
            Program p = new Program();
            DType method = new DType(p.ElMetodoAsincrono);

            IAsyncResult a = method.BeginInvoke("www.devjoker.com", (res) =>
                {
                    string data = method.EndInvoke(res);
                    Console.WriteLine("Esto es lo que tiene data {0}", data);
                }
            , null);

            Console.WriteLine("Después de la llamada asíncrona, Hilo principal");

            Console.ReadLine();

        }       

        string ElMetodoAsincrono(string input)
        {
            Console.WriteLine("Inicio de la ejecución del método \"ElMetodoAsincrono\"");
            Console.WriteLine("El parámetro es -{0}-", input);
            Thread.Sleep(3000);
            Console.WriteLine("Finalización de la ejecución del método \"ElMetodo\".");
            return "Valor de retorno";
        }        
    }    
}

Saludos y hasta la próxima.

DJK

Pedro  Herrarte  Sánchez
Llamadas asíncronas con C#
Pedro Herrarte Sánchez

Pedro Herrarte, es consultor independiente, ofreciendo servicios de consultoría, análisis, desarrollo y formación. Posee mas de diez años de experiencia trabajando para las principales empresas de España. Es especialista en tecnologías .NET, entornos Web (ASP.NET, ASP.NET MVC,jQuery, HTML5), bases de datos (SQL Server y ORACLE) e integración de sistemas. Es experto en desarrollo (C#, VB.Net, T-SQL, PL/SQL, , ASP, CGI , C, Pro*C, Java, Essbase, Vignette, PowerBuilder y Visual Basic ...) y bases de datos (SQL Server y ORACLE). Pedro es MCP y MAP 2012, es fundador, diseñador y programador de www.devjoker.com..
Fecha de alta:02/06/2010
Última actualizacion:03/06/2010
Visitas totales:16918
Valorar el contenido:
Últimas consultas realizadas en los foros
Últimas preguntas sin contestar en los foros de devjoker.com