Programación Asíncrona antes de C#5 – Parte 2

Continuando con esta miniserie de artículos sobre la programación asíncrona y de cómo la habíamos venido trabajando antes de C#5, nos encontramos con otro modelo de programación asíncrona, la no muy conocida pero si muy usada Event-based Asynchronous Pattern o EAP que fue introducida en .NET 2, este patrón como su nombre lo indica se basa en el uso de eventos para informarnos que una tarea asíncrona ha terminado con éxito y poder entregarnos su valor de resultado. La parte asíncrona del asunto se sigue trabajando con un APM común y corriente, solo que al implementar un EAP podemos crear clases más especializadas en el tratamiento del procesamiento asíncrono y en opinión personal, su uso depende de la necesidad.

  • Event-based Asynchronous Pattern (EAP)

En un modelo EAP las operaciones asíncronas se representan por un método NombreMetodoAync y un evento NombreMetodoCompleted.

En la msdn tenemos una completa documentación de EAP en este enlace.

Pero a manera de ejemplo y continuando con la serie que traíamos, voy a explicar de manera muy rápida y sencilla como implementar un modelo EAP.

Al ejemplo que traíamos del post anterior, le he agregado una nueva clase encargada de exponer nuestro método Sumatoria de forma asíncrona así mismo, expondrá el evento encargado de notificarnos que la operación ha sido terminada.

Por el momento el código de nuestra clase sería el siguiente:

    public class OperacionMatematica
    {
        public long Sumatoria(int numero, long nVeces)
        {
            long resultado = 0;
            for (int i = 1; i <= nVeces; i++)
            {
                resultado += numero;
            }
            return resultado;
        }
        public void SumatoriaAsync(int numero, long nVeces)
        {
            throw new NotImplementedException();
        }
    }

Ahora vamos a implementar un modelo APM que será el encargado de ejecutar la tarea de forma asincrónica.

    public class OperacionMatematica
    {
        private delegate long SumatoriaAsyncDelegate(int numero, long nVeces);

        public long Sumatoria(int numero, long nVeces)
        {
            long resultado = 0;
            for (int i = 1; i <= nVeces; i++)
            {
                resultado += numero;
            }
            return resultado;
        }

        public void SumatoriaAsync(int numero, long nVeces)
        {
            SumatoriaAsyncDelegate delegado = Sumatoria;
            delegado.BeginInvoke(numero, nVeces, FinOperacionCallBack, delegado);
        }

        private void FinOperacionCallBack(IAsyncResult ar)
        {
            var delegado = ar.AsyncState as SumatoriaAsyncDelegate;
            if (delegado != null)
            {
                long resultado = delegado.EndInvoke(ar);
            }
        }
    }

Y ahora vamos a exponer el evento encargado de darnos la notificación, como sabemos los eventos son del tipo de un delegado, y sabemos que nuestro evento deberá notificarnos de alguna forma resultado de la invocación, junto con otra información útil, como el estado de la misma. A esto se le conoce como EventArgs y podemos crear una para nuestro ejemplo, solo basta con crear la clase que exponga los miembros que queremos emplear y en nuestro caso implementar de la clase AsyncCompletedEventArgs. El código de nuestra clase queda de la siguiente manera:

    public class SumatoriaAsyncCompletedEventHandlerArgs : AsyncCompletedEventArgs
    {
        private readonly long _result;

        public long Result
        {
            get { return _result; }
        }

        internal SumatoriaAsyncCompletedEventHandlerArgs(long result, Exception error, bool cancelled, object userState)
            : base(error, cancelled, userState)
        {
            _result = result;
        }
    }

Una vez hecho esto podemos definir nuestro delegado.

    public delegate void SumatoriaAsyncCompletedEventHandler(SumatoriaAsyncCompletedEventHandlerArgs args);

Y una vez definido nuestro delegado, podemos crear el evento del tipo de este delegado en nuestra clase de operaciones matemáticas, así:

        public event SumatoriaAsyncCompletedEventHandler SumatoriaAsyncCompleted;

Ahora solo nos resta poder lanzar este evento una vez la tarea asíncrona termine de ejecutarse, para esto creamos un campo del tipo del delegado en nuestra clase y en el CallBack de la operación asíncrona lo invocamos pasando como argumento una nueva instancia de nuestro EventArgs con la información requerida. En este caso y a manera de ejemplo solo usaré la que yo he definido como result. Como paso final crearemos los “accessors” de nuestro evento. El código completo nos queda de la siguiente manera.

    public class OperacionMatematica
    {
        private delegate long SumatoriaAsyncDelegate(int numero, long nVeces);

        private SumatoriaAsyncCompletedEventHandler _sumatoriaCompleted;

        public long Sumatoria(int numero, long nVeces)
        {
            long resultado = 0;
            for (int i = 1; i <= nVeces; i++)
            {
                resultado += numero;
            }
            return resultado;
        }

        public void SumatoriaAsync(int numero, long nVeces)
        {
            SumatoriaAsyncDelegate delegado = Sumatoria;
            delegado.BeginInvoke(numero, nVeces, FinOperacionCallBack, delegado);
        }

        private void FinOperacionCallBack(IAsyncResult ar)
        {
            var delegado = ar.AsyncState as SumatoriaAsyncDelegate;
            if (delegado != null)
            {
                long resultado = delegado.EndInvoke(ar);
                _sumatoriaCompleted(new SumatoriaAsyncCompletedEventHandlerArgs(resultado, nullfalsenull));
            }
        }

        public event SumatoriaAsyncCompletedEventHandler SumatoriaAsyncCompleted
        {
            add { _sumatoriaCompleted += value; }
            remove { _sumatoriaCompleted -= value; }
        }
    }

El código completo de todas las clases del ejemplo hasta ahora:

    public class OperacionMatematica
    {
        private delegate long SumatoriaAsyncDelegate(int numero, long nVeces);

        private SumatoriaAsyncCompletedEventHandler _sumatoriaCompleted;

        public long Sumatoria(int numero, long nVeces)
        {
            long resultado = 0;
            for (int i = 1; i <= nVeces; i++)
            {
                resultado += numero;
            }
            return resultado;
        }

        public void SumatoriaAsync(int numero, long nVeces)
        {
            SumatoriaAsyncDelegate delegado = Sumatoria;
            delegado.BeginInvoke(numero, nVeces, FinOperacionCallBack, delegado);
        }

        private void FinOperacionCallBack(IAsyncResult ar)
        {
            var delegado = ar.AsyncState as SumatoriaAsyncDelegate;
            if (delegado != null)
            {
                long resultado = delegado.EndInvoke(ar);
                _sumatoriaCompleted(new SumatoriaAsyncCompletedEventHandlerArgs(resultado, nullfalsenull));
            }
        }

        public event SumatoriaAsyncCompletedEventHandler SumatoriaAsyncCompleted
        {
            add { _sumatoriaCompleted += value; }
            remove { _sumatoriaCompleted -= value; }
        }
    }

    public delegate void SumatoriaAsyncCompletedEventHandler(SumatoriaAsyncCompletedEventHandlerArgs args);

    public class SumatoriaAsyncCompletedEventHandlerArgs : AsyncCompletedEventArgs
    {
        private readonly long _result;

        public long Result
        {
            get { return _result; }
        }

        internal SumatoriaAsyncCompletedEventHandlerArgs(long result, Exception error, bool cancelled, object userState)
            : base(error, cancelled, userState)
        {
            _result = result;
        }
    }

Y para usarlo desde nuestra interfaz de usuario en el evento click de un botón tenemos el siguiente código:

        private void BtnCalcAsincEventosClick(object sender, EventArgs e)
        {
            var operacionMatematica = new OperacionMatematica();
            operacionMatematica.SumatoriaAsyncCompleted += CompletadoMatematicas;
            operacionMatematica.SumatoriaAsync(2, 100000);
        }

        private void CompletadoMatematicas(SumatoriaAsyncCompletedEventHandlerArgs args)
        {
            long resultado = args.Result;
            MessageBox.Show(resultado.ToString());
        }

Hasta aquí este post, en próximos días continúo con la serie.

Espero les sea de utilidad.

Hasta el próximo post

Anuncios
Programación Asíncrona antes de C#5 – Parte 2

Un comentario en “Programación Asíncrona antes de C#5 – Parte 2

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 )

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 )

Google+ photo

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

Conectando a %s