[.net] Concurrencia y procesos asíncronos

Una de las cosas que más quebraderos de cabeza nos pueden ocasionar a todos nosotros es la concurrencia de usuarios en las aplicaciones que diseñemos. Por suerte, además de conocer los algoritmos más típicos (Panadería, Dekker, Patterson, Productor-Consumidor…), .NET nos facilita bastante la implementación de esta tarea.

 

Hace pocos días en un diseño técnico de una aplicación Web, el cliente nos plantaba un proceso crítico que tendría la aplicación y que debería ser estable pasase lo que pasase. Además, con una dificultad añadida, el proceso podía durar horas y al finalizar debería mostrar un mensaje al usuario.

 

Dado esto, encontré la solución más viable para el proceso: trabajar en segundo plano sincrónicamente.

 

Para ello, deberemos hacer uso de los delegados y de las librerías  System.Threading, System.Runtime.Remoting.Messaging y de  System.Runtime.CompilerServices.

 

Como primer paso, además de crear una nueva aplicación de consola en C#, deberemos crear una clase con un delegado:

 

public class ClassAsyncCallback

{

      delegate bool deleg();

}

 

El segundo paso, es crear los métodos que se van a usar en el proceso síncrono dentro de la clase; uno que llamará a la función que va a realizar el trabajo y otro que será llamado al terminar el primer proceso:

 

[MethodImpl(MethodImplOptions.Synchronized)]

public bool ProcessCritial()

{

//Ejecutamos el proceso   

}

 


Acordaros de que un delegado es tipo que hace referencia a un método, es decir, el comportamiento del delegado y el método es exactamente el mismo y además, este debe tener la misma firma, valor devuelto, parámetros…que el método.

 

En este método, aparece algo nuevo, es el atributo declarativo en la declaración del método [MethodImpl(MethodImplOptions.Synchronized)]. Este atributo sirve para indicar que ese método estará sincronizado y que todo lo implementado en él es un proceso crítico. Funciona del mismo modo que la clase Monitor, internamente, declara un semáforo que gestiona la entrada única de un subproceso en él método y pone en espera al resto. Al finalizar, libera el método y deja entrar al siguiente proceso. Así sucesivamente.

 

El motivo de utilizar este atributo en vez la clase Monitor es muy simple, este atributo bloquea el método hasta que devuelve un valor, que en mi caso es lo que necesitaba,  además que Monitor nos condiciona a usar además lock para el bloqueo de una variable para saber si el método esta libre o no y como bien podéis deducir, no es una buena practica.

 

Bien, ahora solo nos queda crear el método que mostrara el mensaje al usuario:

 

public void ProcessFinish(IAsyncResult ar)

{

//Mostramos alerta al usuario

}

 

Como veis, este proceso recibe como parámetro IAsyncResult que nos indica el estado de la operación asíncrona.

 

Por ahora, nos centraremos en como llamar de forma asíncrona estos métodos des del Main de la aplicación:

 

static void Main(string[] args)

{

      ClassAsyncCallback Mvar = new ClassAsyncCallback();

      deleg call = new deleg(Mvar.ProcessCritial);

AsyncCallback cb = new AsyncCallback(Mvar.ProcessFinish);

      IAsyncResult ar = call.BeginInvoke(cb, null);

}

 

Bien, llegados hasta aquí ya tenemos la forma de ejecutar el proceso asíncrono preparado para la concurrencia de usuarios. Como veis, deberemos crear una nueva instancia del delegado declarado anteriormente y pasarle el nombre del método. Con la clase AsyncCallback y pasándole el nombre del método,  ya tendremos el objeto que realizara la llamada.

 

Mediante IAsyncResult y con BeginInvoke, ejecutaremos el proceso asíncrono. Como primer parámetro le pasaremos el método que se debe llamar al finalizar y con el objeto delegado llamaremos el método que debe ser llamado asincrónicamente.

 

Tal y como hemos dicho, ProcessFinsih es el encargado de mostrar la alerta al usuario según el resultado del método ProcessCrtical. Para recoger el valor devuelto por este dentro de ProcessFinsih, debemos modificar el método de la siguiente forma:

 

public void ProcessFinish(IAsyncResult ar)

{

 

deleg resultado = (deleg)((AsyncResult)ar).AsyncDelegate;

if (resultado.EndInvoke(ar))

      //Mostramos alerta al usuario

}

 

Con AsyncDelegate lo que hacemos es recoger el objeto delegado que se ha invocado en la llamada asíncrona y se debe convertir al tipo de delegado declarado por nosotros para poder obtener el resultado de ProcessCrtical; si es true le mostraremos la alerta al usuario.

 

No es muy complicado ni nada dificultoso, hay que decir que este tipo de problemas es muy típico en bastantes aplicaciones, sobretodo si hay procesos que requieran una gran cantidad de tiempo. Además de solventar un posible problema de concurrencia de usuarios, evitamos que nuestro cliente tenga que esperar x tiempo a que termine este proceso para seguir trabajando con la aplicación por lo que mejoramos el rendimiento de la misma.

 

Pues nada, a disfrutarlo y ya sabéis… si hay dudas, preguntar.

 

¡¡Enjoy!!

Published 26/5/2009 20:48 por Francesc Jaumot
Comparte este post:

Comentarios

# re: [.net] Concurrencia y procesos asíncronos

Tuesday, May 26, 2009 8:55 PM por Unai

Frances, en principio no es muy buena idea recurrir a las llamadas a BeginInvoke y EndInvoke de los delegados puesto que estos incurren en uso de plataforma de Remoting y el rendimiento podría verse afectado. Suele ser mejor construirse el propio patrón de asincronía en .NET..

Saludos

Unai

# re: [.net] Concurrencia y procesos asíncronos

Tuesday, May 26, 2009 9:08 PM por Francesc Jaumot

¡Hola Unai!

Pues no tenía conocimiento de que usar esto así tuviera penalización en el rendimiento. Sobre lo que comentas de implementar un propio patrón de asíncrona en .NET, me parece interesante, pero debería haber alguna forma más práctica de hacer lo que propongo en el ejemplo usando únicamente el Framework de .NET sin penalizar el rendimiento.

¿Alguna propuesta?

# re: [.net] Concurrencia y procesos asíncronos

Tuesday, May 26, 2009 9:35 PM por Unai

.NET solamente propone el patrón y te da las bases para que tu lo implementes, BeginInvoke y EndInvoke es una implementación del patrón, pero no quita que tu puedas hacer la tuya, de hecho como comenté suele ser lo recomendable

# re: [.net] Concurrencia y procesos asíncronos

Tuesday, May 26, 2009 9:37 PM por Luis Guerrero

Hola Francesc, estupendo articulo el tuyo enhorabuena.

Solamente comentar una pequeña cosa, cuando comentas en tú artículo de usar [MethodImpl(MethodImplOptions.Synchronized)] tienes que tener en cuenta que es lo mismo que llamar a Monitor.Enter(this) o lock(this) y fíjate que utilizo this como objeto de bloqueo porque es justamente lo que hace ese atributo, si el método fuera estático sería Monitor.Enter(typeof(MiTipo)) o lock(typeof(MiTipo) lo que es algo mucho peor.

Lo ideal para estos casos es utiliza una varible de tipo object y utilizar esa variable como objeto de bloqueo, intentando que el tiempo que el método se pasa bloqueado sea el mínimo posible para aumentar así el tiempo de respuesta del sistema.

Yo lo hubiera implementado con ThreadPool.QueueWorkItem para hacerlo asíncrono.

Saludos. Luis.

# re: [.net] Concurrencia y procesos asíncronos

Tuesday, May 26, 2009 10:26 PM por Francesc Jaumot

Gracias por las aportaciones a los dos! Ambas muy interesantes!

Saludos.Francesc.

# re: [.net] Concurrencia y procesos asíncronos

Tuesday, May 26, 2009 11:29 PM por Marcelo

Creo que este blog promete, con comentarios de tantos cracks..

Muy bueno el articul y muy buenos comentarios.

# re: [.net] Concurrencia y procesos asíncronos

Wednesday, May 27, 2009 5:07 PM por Andres

Suele ser mejor construirse el propio patrón de asincronía en .NET.., como se haria eso?

# re: [.net] Concurrencia y procesos asíncronos

Wednesday, May 27, 2009 6:17 PM por Unai

Aquí tienes un ejemplo de implementación de IAsyncResult para hacer el patron de asincronía de .NET y de paso, también usando ThreadPool.QueueUserWorkItem

blogs.msdn.com/.../AGenericIAsyncResultImplementation.aspx

Unai

# re: [.net] Concurrencia y procesos asíncronos

Thursday, May 28, 2009 12:45 AM por Francesc Jaumot

Gracias Unai!

De todas formas estoy preparando un post con una implementación que uso parecida a la de MSDN para que se comprenda mejor el porqué de implementar IAsyncResult.

francesc

# re: [.net] Concurrencia y procesos asíncronos

Saturday, August 22, 2009 6:40 PM por Jesús Bosch

te parecerá una pregunta tonta... y quizá lo sea xD pero como haces para que el código se vea en colores en geeks?