vtortola.Net

Mayo 8, 2007

Modelo Asíncrono. Parte III, espera y finalización.

Archivado en: .NET, C#, Modelo Asíncrono — vtortola @ 11:17 am

Siguiendo con el modelo asíncrono, hoy explicaré como esperar a un thread en concreto y como realizar una finalización síncrona de forma adecuada.
Para finalizar una operación asíncrona de forma síncrona (bloqueando el hilo actual hasta que el secundario termine) podemos hacerlo de dos formas, llamando al método .EndInvoke del objeto delegado, con el inconveniente de que si tenemos definido un
AsyncCallback y hay otro .EndInvoke allí recibiremos una “InvalidOperationException en alguno de los dos sitios con el mensaje “EndInvoke can only be called once for each asynchronous operation. que viene a decir “EndInvoke solo puede ser llamado una vez por cada operación asíncrona con la molestia que ello conlleva; la forma elegante es utilizar el método “IAsyncResult. AsyncWaitHandle.WaitOne” que causará la finalización síncrona pero sin modificar el comportamiento que habíamos previsto en un principio, de esta forma podemos forzar la espera a la finalización de un determinado hilo ó grupo de hilos que por ejemplo realizasen tareas que habría que completar inevitablemente para poder continuar, con la ventaja de que se ejecutarían de forma paralela.

El ejemplo esta basado en el del ejemplo anterior, Modelo Asíncrono. Parte II, control.

private delegate void AsyncCallOut_(string p1, int p2, out StringBuilder parametroSalida);
private static AsyncCallOut_ AsynCallOut;

/// <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)
{

AsynCallOut = new AsyncCallOut_(AsyncFuncOut);

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

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

// Me quedo con la última tarea asíncrona.
ia=AsynCallOut.BeginInvoke(string.Format(“Prueba 1.{0}”, i), i, out sb, new AsyncCallback(FinAsyncFuncOut), string.Format(“objectstate_{0}”, i));

}

// Si no esta completada …
if (!ia.IsCompleted)
{

// … espero a que termine.
ia.AsyncWaitHandle.WaitOne();

}
Console.WriteLine(“Ultima tarea completada: {0}”, ia.IsCompleted == true ? “Si” : “No”);

Console.ReadKey();

}

/// <summary>
/// Método que se ejecutará de forma asíncrona para el 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);

}
catch (Exception Ex)
{

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

}

}

El resultado seria algo tal que así:

-> Lanzando tarea asíncrona 0.

-> 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.0 en AsyncFuncOut.

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′.

Ultima tarea completada: Si

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′.

 

Podemos ver que el avance de Main se ha detenido en la espera que hemos programado hasta que la última tarea (por coger una) ha finalizado, esto es útil como comentaba para controlar el avance de los hilos de forma que las tareas que inevitablemente tienen que ser secuenciales puedan realizarse en hilos separados y luego esperar a que se den las condiciones oportunas para continuar.

Para el siguiente artículo explicaré la sincronización y el acceso concurrente a recursos.

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.