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
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); /// <summary>
} /// <summary>
} /// <summary>
} |
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. |






