|
Page 1 of 5 Introducción a C.C.R.: Concurrency and Coordination Runtime
Un aspecto muy importante en el desarrollo de una aplicación de control para un robot es la coordinación de múltiples tareas interactivas y simultáneas. Este paradigma se conoce como programación asíncrona y concurrente. Como se ha comentado anteriormente, MSRS utiliza las funciones definidas en el CCR para mitigar la complejidad de este tipo de entornos. El CCR ofrece un gran número de clases que permiten al programador expresar fácilmente diversos modelos de coordinación. Existen tres conceptos o componentes claves del CCR que merece la pena estudiar en detalle:
- Los puertos (clases Port y PortSet).
- Las primitivas de coordinación (clase Arbiter).
- Las tareas y colas de ejecución (clases Dispatcher, DispatcherQueue y Task).
La clase puerto (Port) de CCR es la primitiva que se usa más comúnmente al programar con MSRS. La clase Port es en esencia una cola FIFO de elementos (objetos) que implementa los interfaces IPortReceive e IPort. Estas interfaces separan los métodos que añaden elementos al puerto de los que los recuperan. Hay que tener en cuenta que añadir un elemento a un puerto es una tarea asíncrona. La tarea de recuperación de un elemento de un puerto se puede dar en dos escenarios distintos:
- Si el puerto se usa como una cola pasiva, no se realiza ninguna tarea cuando se ha enviado algún elemento al puerto. Los elementos se pueden recuperar llamando al método Test, que devuelve el valor booleano false y un objeto null si no existe ningún elemento. Este escenario se usa cuando se tiene algún otro mecanismo para ejecutar el código del evento y lo único que se busca es el uso eficiente de una cola FIFO. Además, proporciona la flexibilidad de añadir un receptor al puerto posteriormente.
- Si el puerto se usa como una cola activa, las tareas están programadas del tal forma que se ejecutan cuando hay algún elemento que se ha enviado al puerto. En este caso, como se verá a continuación, uno o más Arbiters (o coordinadores) tienen registrado el puerto. Este es el caso más común de uso del puerto al programar con CCR.
En cuanto a PortSet, tiene la misma funcionalidad de Port, pero con la diferencia de que acepta varias clases de elementos. Es decir, es un puerto que representa diferentes tipos de datos y puede recibir cualquiera de ellos. Habitualmente se crea un PortSet que soporta N tipos diferentes y es capaz de coordinar los diferentes mensajes independientemente.
Las primitivas de coordinación o Arbiters son las clases que coordinan la ejecución del código. El CCR permite y promueve la concurrencia proporcionando múltiples maneras de expresar lo que debería suceder de forma coordinada. Esto se consigue a través de los mensajes recibidos en los puertos. Hay dos escenarios en los que el CCR permite la coordinación del flujo de ejecución del programa:
- Coordinación de peticiones de entrada. Por ejemplo, la espera a una respuesta HTTP en un puerto de red. - Coordinación de respuesta de una o más peticiones. Por ejemplo, la espera de la finalización de ejecución de una función con éxito o con error.
Para trabajar con las primitivas de coordinación se usa la clase Arbiter que proporciona diversos métodos estáticos como los siguientes:
Arbiter.FromTask - Crea instancias de Task. Arbiter.Choice - Crea instancias de Choice. Arbiter.Receive - Crea instancias de Receive. Arbiter.Interleave - Crea instancias de Interleave.
Un receptor de un elemento simple asocia un delegado (delegate – o método anónimo) de usuario cuya entrada es un parámetro simple de tipo T, donde la instancia sería Port<T>. Si la opción de persistencia es true, el receptor ejecutará el delegado por cada elemento enviado. Si por el contrario, la opción de persistencia es false, el receptor sólo ejecutará el delegado para un elemento y luego se retirará del puerto.
A continuación se explican brevemente algunas de las primitivas de coordinación más usadas en el CCR. Arbiter.Choice es importante, ya que permite crear una jerarquía de Arbiters y así invocarlos en el orden correcto, comprobando anteriormente, si el código de usuario está preparado para ser ejecutado. Esto permite al programador, expresar una coordinación compleja en apenas unas líneas de código. Además, es un patrón que especifica una elección entre diferentes tareas a ejecutar. Otro método de coordinación importante que implementa Arbiter es Interleave. En este caso el CCR está pendiente de las respuestas y activaciones hasta que el proceso ha terminado. El programador sólo tiene que especificar que protección necesita. Esta es la solución que se propone en MSRS al acceso de recursos privados que habitualmente ocurre por no estar debidamente protegidos.
Cuando tenemos múltiples elementos receptores, podemos distinguir dos posibles escenarios:
- Los conocidos como Joins son receptores que están preparados para recibir de uno o más puertos, si uno de los intentos falla, vuelven a ponerlo en la cola y esperan un tiempo hasta que las condiciones sean propicias para conseguirlo con éxito. Se puede usar para garantizar acceso a recursos múltiples. El número de puertos y de elementos se puede establecer en tiempo de compilación o en tiempo de ejecución.
- Receptores que eliminan elementos por cada puerto que participa en la recepción. Cuando se llega a un número satisfactorio de elementos, se ejecuta el delegado. Se suele utilizar en escenarios en los que halla múltiple respuestas pendientes.
Las tareas y colas de ejecución son también elementos principales del CCR. Las tareas se generan cuando los mensajes llegan a los puertos. Para orquestar los recursos de ejecución se cuenta con las siguientes clases:
- La interfaz ITask y las implementaciones Task e IterativeTask. Solamente las clases que implementan ITask se pueden programar. Los Arbiters son un ejemplo de ello, es decir, se pueden programar y activar correctamente.
- La clase de DispatcherQueue. DispatcherQueue implementa una cola FIFO de Task (tareas), es decir, delgados que identifican los métodos que se pueden ejecutar.
- La clase Dispatcher. Esta clase maneja las tareas de los hilos del SO y el desencolado de unas o más instancias DispatcherQueue.
|