vtortola.Net

Abril 29, 2007

Modelo Asíncrono. Parte II, control.

Archivado en: .NET, C#, Modelo Asíncrono, Recurso MSDN — vtortola @ 4:36 pm

Como continuación de Modelo Asíncrono. Parte I, invocación, hoy ejemplificaré como controlar el flujo de operaciones asincrónas asegurando el que no haya más de cierto número de operaciones en ejecución al mismo tiempo. Como anteriormente, recomiendo leer la documentación sobre el modelo asíncrono de MSDN.

¿Cómo controlar el nº de operaciones asíncronas que están en ejecución? Si estamos sobre una máquina SMP y cada thread (cada operación asíncrona se lanza en un thread en segundo plano) consume una CPU por completo por tratarse de una operación local (ie: manejo de archivos de texto, descifrar archivos, etc..) ó cada thread provoca que un servicio de la máquina consuma una CPU entera (ie: realizar una conexión a Oracle y lanzar una query tremebunda) es importante poder controlar en todo momento cuando son lanzadas esas operaciones, que número de operaciones concurrentes hay en cada momento y cuando lanzar más.

Podemos hacerlo también modificando el número máximo de tareas simultaneas que puede alojar el ThreadPool, pero eso afectaría a toda la aplicación y quizás nosotros solo queremos limitar a X en una parte del programa … y a Z en otra :P

Para ello nos ayudaremos de IAsyncResult , la interfaz que cumplen los objetos que son devueltos por cualquier método asíncrono (ie: .BeginXXX). Esta interfaz contiene unas propiedades que nos permitirán conocer el estado de la operación e incluso forzar una finalización síncrona.

Tomando el ejemplo de la entrega pasada, he realizado unos cambios en el método Main para ejemplificar como controlar el nº de operaciones asíncronas simultáneas, he añadido globalmente al programa un List<IAsyncResult> donde almacenaré los IAsyncResult devueltos y he añadido al método configurado como AsyncCallback la capacidad de eliminar el IAsyncResult de la lista. De esta forma mantengo una lista de las tareas asíncronas en ejecución, impido que se ejecuten más del máximo que yo he impuesto y conforme van terminando se van ejecutando más.

Nótese el uso de la instrucción lock, que garantiza que la sección de código que contiene entre llaves solo será accedida por un thread cada vez bloqueando a los demás hasta que abandone dicha sección. Es vital asegurar que nunca acceden dos threads al mismo objeto con intención de modificarlo ya que podrian dejarlo en un estado inconsistente. Hablaré de sincronización más adelante.

private delegate void AsyncCallOut_(string p1, int p2, out StringBuilder parametroSalida);
private static AsyncCallOut_ AsynCallOut;
private static List<IAsyncResult> AsyncTasks;

/// <summary>
/// Punto de entrada de la aplicación.
/// </summary>
/// <param name=”args”>Argumentos pasados como parámetros al ejecutable.</param>
static void Main(string[] args)
{

AsyncTasks = new List<IAsyncResult>();
AsynCallOut =
new AsyncCallOut_(AsyncFuncOut);

StringBuilder sb;
for(int i=0;i<10;i++)
{

Console.WriteLine(“–> Lanzando tarea asíncrona {0}.”, i);

// Cada llamada a .BeginInvoke devuelve un IAsyncResult,
// guardo esos IAsyncResult para poder controlar después
// el estado y la finalización de la operación.
AsyncTasks.Add(AsynCallOut.BeginInvoke(string.Format(“Prueba 1.{0}”, i), i, out sb, new AsyncCallback(FinAsyncFuncOut), string.Format(“objectstate_{0}”, i)));

// Hago un bucle de espera hasta que haya menos de 2 tareas
// en la lista de tareas. Asi no habrá nunca más de dos threads
// en segundo plano.
while (AsyncTasks.Count > 1)
{

System.Threading.Thread.Sleep(100);

}

}

// Bloqueo la ejecución hasta que no haya tareas en la lista
// por acabar para que no finalize el programa habiendo tareas
// asíncronas en ejecución.
while (AsyncTasks.Count > 0)
{

System.Threading.Thread.Sleep(100);

}

Console.ReadKey();

}

/// <summary>
/// Método que se ejecutará de forma asíncrona para el segundo ejemplo.
/// </summary>
/// <param name=”parametro”>Cadena de entrada.</param>
/// <param name=”sb”>Parámetro de salida.</param>
private static void AsyncFuncOut(string p1, int p2, out StringBuilder sb)
{

sb = new StringBuilder();

sb.AppendFormat(“{0} en AsyncFuncOut.”, p1);
Console.WriteLine(sb);

Random rndm = new Random(p2);
System.Threading.
Thread.Sleep(rndm.Next(5000, 10000));

}

/// <summary>
/// Método que se ejecutará al finalizar la tarea asíncrona del primer ejemplo.
/// </summary>
/// <param name=”IA”>IAsyncResult</param>
private static void FinAsyncFuncOut(IAsyncResult IA)
{

try
{

StringBuilder sb;
AsynCallOut.EndInvoke(
out sb, IA);

Console.WriteLine(“Fin de AsyncFunc con resultado: ‘{0}’ con objectState : ‘{1}’”,sb, IA.AsyncState);

// Borro la tarea en cuestión de la lista.
lock (AsyncTasks)
{

AsyncTasks.Remove(IA);

}

}
catch (Exception Ex)
{

Console.WriteLine(“EX:{0}\n\n{1}”, Ex.Message, Ex.StackTrace);

}

}

La ejecución de este código daria un resultado similar al siguiente:

–> Lanzando tarea asíncrona 0.

Prueba 1.0 en AsyncFuncOut.

–> Lanzando tarea asíncrona 1.

Prueba 1.1 en AsyncFuncOut.

Fin de AsyncFunc con resultado: ‘Prueba 1.1 en AsyncFuncOut.’ con objectState : ‘objectstate_1′

–> Lanzando tarea asíncrona 2.

Prueba 1.2 en AsyncFuncOut.

Fin de AsyncFunc con resultado: ‘Prueba 1.0 en AsyncFuncOut.’ con objectState : ‘objectstate_0′

–> Lanzando tarea asíncrona 3.

Prueba 1.3 en AsyncFuncOut.

Fin de AsyncFunc con resultado: ‘Prueba 1.3 en AsyncFuncOut.’ con objectState : ‘objectstate_3′

–> Lanzando tarea asíncrona 4.

Prueba 1.4 en AsyncFuncOut.

Fin de AsyncFunc con resultado: ‘Prueba 1.2 en AsyncFuncOut.’ con objectState : ‘objectstate_2′

–> Lanzando tarea asíncrona 5.

Prueba 1.5 en AsyncFuncOut.

Fin de AsyncFunc con resultado: ‘Prueba 1.5 en AsyncFuncOut.’ con objectState : ‘objectstate_5′

–> Lanzando tarea asíncrona 6.

Prueba 1.6 en AsyncFuncOut.

Fin de AsyncFunc con resultado: ‘Prueba 1.4 en AsyncFuncOut.’ con objectState : ‘objectstate_4′

–> Lanzando tarea asíncrona 7.

Prueba 1.7 en AsyncFuncOut.

Fin de AsyncFunc con resultado: ‘Prueba 1.7 en AsyncFuncOut.’ con objectState : ‘objectstate_7′

–> Lanzando tarea asíncrona 8.

Prueba 1.8 en AsyncFuncOut.

Fin de AsyncFunc con resultado: ‘Prueba 1.6 en AsyncFuncOut.’ con objectState : ‘objectstate_6′

–> Lanzando tarea asíncrona 9.

Prueba 1.9 en AsyncFuncOut.

Fin de AsyncFunc con resultado: ‘Prueba 1.9 en AsyncFuncOut.’ con objectState : ‘objectstate_9′

Fin de AsyncFunc con resultado: ‘Prueba 1.8 en AsyncFuncOut.’ con objectState : ‘objectstate_8′

Fin de programa.

Mientras que si eliminasemos el mecanismo de contención seria este:

–> Lanzando tarea asíncrona 0.

Prueba 1.0 en AsyncFuncOut.

–> Lanzando tarea asíncrona 1.

–> Lanzando tarea asíncrona 2.

–> Lanzando tarea asíncrona 3.

–> Lanzando tarea asíncrona 4.

–> Lanzando tarea asíncrona 5.

–> Lanzando tarea asíncrona 6.

–> Lanzando tarea asíncrona 7.

–> Lanzando tarea asíncrona 8.

–> Lanzando tarea asíncrona 9.

Prueba 1.1 en AsyncFuncOut.

Prueba 1.2 en AsyncFuncOut.

Prueba 1.3 en AsyncFuncOut.

Prueba 1.4 en AsyncFuncOut.

Prueba 1.5 en AsyncFuncOut.

Prueba 1.6 en AsyncFuncOut.

Prueba 1.7 en AsyncFuncOut.

Prueba 1.8 en AsyncFuncOut.

Prueba 1.9 en AsyncFuncOut.

Fin de AsyncFunc con resultado: ‘Prueba 1.1 en AsyncFuncOut.’ con objectState : ‘objectstate_1′

Fin de AsyncFunc con resultado: ‘Prueba 1.3 en AsyncFuncOut.’ con objectState : ‘objectstate_3′

Fin de AsyncFunc con resultado: ‘Prueba 1.0 en AsyncFuncOut.’ con objectState : ‘objectstate_0′

Fin de AsyncFunc con resultado: ‘Prueba 1.5 en AsyncFuncOut.’ con objectState : ‘objectstate_5′

Fin de AsyncFunc con resultado: ‘Prueba 1.2 en AsyncFuncOut.’ con objectState : ‘objectstate_2′

Fin de AsyncFunc con resultado: ‘Prueba 1.7 en AsyncFuncOut.’ con objectState : ‘objectstate_7′

Fin de AsyncFunc con resultado: ‘Prueba 1.4 en AsyncFuncOut.’ con objectState : ‘objectstate_4′

Fin de AsyncFunc con resultado: ‘Prueba 1.9 en AsyncFuncOut.’ con objectState : ‘objectstate_9′

Fin de AsyncFunc con resultado: ‘Prueba 1.6 en AsyncFuncOut.’ con objectState : ‘objectstate_6′

Fin de AsyncFunc con resultado: ‘Prueba 1.8 en AsyncFuncOut.’ con objectState : ‘objectstate_8′

Fin de programa.

No Comments Yet »

Aún no hay comentarios.

Canal RSS de los comentarios de la entrada. URI para TrackBack.

Deja un comentario

Debes ser Sesión como para publicar un comentario.

Blog de WordPress.com.