7/31/2019 Capitulo 7. Programacion concurrente
1/9
1
CAPTULO 7. PROGRAMACIN CONCURRENTE
7 Programacin concurrente. ............................................................................................2
7.1 La nocin de proceso .............................................................................................2
7.1.1 Construcciones de programacin concurrente ............................................... 4
7.2 Ejecucin concurrente............................................................................................ 4
7.2.1 Procesos y objetos ..........................................................................................6
7.3 Representacin de procesos ................................................................................... 7
7.3.1 Corrutinas....................................................................................................... 7
7.3.2 Fork y join ...................................................................................................... 7
7.3.3 Cobegin .......................................................................................................... 8
7.3.4 Declaracin explicita de procesos.................................................................. 8
Rafael lvarez Garcaltima revisin [email protected]
Nota importante:
Este documento no pretende reemplazar al material propuesto por la UNED para la
asignatura Sistemas en Tiempo Real.
Cualquier sugerenca, comentario o correccin sobre este documento, envelo [email protected] para poder realizar los cambios necesarios.
7/31/2019 Capitulo 7. Programacion concurrente
2/9
2
7 Programacin concurrente.
Virtualmente, todos los sistemas de tiempo real son inherentemente concientes. Los lenguajes
destinados a ser usados en este dominio tienen mayor potencia expresiva si proporcionan al
programador primitivas que se ajusten al paralelismo de las aplicaciones.
Denominamos programacin concurrente a la notacin y tcnicas de programacin que
expresan el paralelismo potencia] y que resuelven los problemas resultantes de la
sincronizacin y la comunicacin. La implementacin del paralelismo es un tena de los
sistemas informticos (hardware y software) esencialmente independiente de la
programacin concurrente. La importancia de la programacin concurrente esta en que
proporciona un entorno abstracto donde estudiar el paralelismo sin tener que enfrascarse en
los detalles de implementacin, (Ben-Ari, 1982)
7.1 La nocin de proceso
Cualquier lenguaje, natural o informtico, tiene un carcter dual: a la vez que es capaz de
expresarse, tambin limita el entorno en el que se aplica dicha capacidad expresiva. Si un lenguaje no
permite cierta nocin o concepto particular, entonces aquellos que utilizan el lenguaje no podrn
emplear este concepto, y puede que, incluso, desconozcan por completa su existencia.
Pascal, C, FORTRAN y COBOL comparten la propiedad comn de ser lenguajes de programacin
secuenciales. Los programas escritos en estos lenguajes tienen un nico hilo de control. Comienzan la
ejecucin en cierto estado y avanzan ejecutando una sentencia cada vez, hasta que el programa
finaliza. La traza a travs del programa puede diferir debido a variaciones en los datos de entrada,
aunque para una ejecucin concreta del programa existe una nica traza. Esto no es lo adecuado para la
programacin de sistemas de tiempo real. Un programa concurrente puede verse como un conjunto de
procesos secuenciales autnomos, que son ejecutados en paralelo. Todos los lenguajes de
programacin concurrente incorporan, explicita o implcitamente, la nocin de proceso; cada. proceso
tiene un hilo de control.
La implementacin real (esto es, la ejecucin) de un conjunto de procesos tiene lugar normalmente
de tres formas. Los procesos pueden;
(1) Multiplexar sus ejecuciones sobre un nico procesador.
(2) Multiplexar su ejecuciones en un sistema multiprocesador con acceso a memoria
compartida.
(3) Multiplexar sus ejecuciones en diversos procesadores que no comparten memoria (a estos
sistemas se les denomina, normalmente, sistemas distribuidos).
Tambin es posible encontrar hbridos de estos tres mtodos.
Solo en los casos (2) y (3) es posible una verdadera ejecucin paralela de mas de un proceso. El
termino concurrente indica paralelismo potencial. Los lenguajes de programacin concurrente
permiten al programador expresar actividades lgicamente paralelas sin tener en cuenta su
implementacin.
Considerando as los procesos, queda claro que la ejecucin de un programa concurrente no es tan
directa como la ejecucin de un programa secuencial. Los procesos deben ser creados y finalizados, as
7/31/2019 Capitulo 7. Programacion concurrente
3/9
3
como distribuidos hacia / desde los procesadores disponibles. Estas actividades son efectuadas por el
sistema de soporte de ejecucin (RTSS; Run-Time Support System) o ncleo de ejecucin. El RTSS
posee muchas de las caractersticas del planificador de un sistema operativo, y esta ubicado,
lgicamente, entre el hardware y el software de aplicacin. En realidad, puede tomar una de las
siguientes formas:
(1) Una estructura software programada como parte de la aplicaci6n (esto es, como un
componente del programa concurrente). Esta es la aproximacin adoptada por el lenguaje
Modula-2.
(2) Un sistema software estndar generado junto al cdigo objeto del programa por el
compilador. Esta es la estructura habitual en los programas de Ada y Java.
(3) Una estructura hardware micro codificada en el procesador, por motivos de eficiencia. Un
programa occam2 que se ejecuta en un transputer tiene este sistema de ejecucin.
El algoritmo que utiliza el RTSS para planificar (esto es, para decidir que proceso se ejecuta a
continuacin en el caso de haber mas de uno ejecutable) afectara al comportamiento temporal del
programa, aunque, para programas bien construidos, el comportamiento lgico no depender del
RTSS. Desde el punto de vista del programa, se asume que el RTSS planifica los procesos de forma no
determinista. En el caso de los sistemas de tiempo real las caractersticas de la planificacin sonrelevantes.
Todos los sistemas operativos proporcionan mecanismos para crear procesos concurrentes.
Normalmente, cada proceso se ejecuta en su propia maquina virtual, para evitar interferencias con
otros procesos no relacionados. Cada proceso consta, realmente, de un nico programa. Sin embargo,
en los ltimos aos se tiende a permitir la creacin de procesos dentro de los programas. Los sistemas
operativos modernos permiten crear procesos dentro del mismo programa accediendo de modo
compartido, y sin restricciones, a la memoria comn (estos procesos suelen llamarse hilos o hebras).
Por tanto. en los sistemas operativos que se ajustan a POS1X. es preciso distinguir entre la
concurrencia de programas (procesos), y la concurrencia dentro de un programa (hilos). tambin es
frecuente distinguir entre aquellos hilos visibles desde el sistema operativo y aquellos que provienen
nicamente del soporte de ciertas rutinas de biblioteca.
Ha habido un amplio debate entre programadores, diseadores de lenguajes y diseadores de
sistemas operativos, sobre si lo apropiado es que sea el lenguaje quien de soporte para la concurrencia,
o si ste debiera ser proporcionado nicamente por el sistema operativo. Los argumentos a favor de
incluir la concurrencia en los lenguajes de programacin son los siguientes:
(1) Lleva a programas mas legibles y fciles de mantener.
(2) Existen muchos tipos distintos de sistemas operativos; al definir la concurrencia en el len-
guaje se consiguen programas mas portables.
(3) Puede que el computador embebido ni siquiera disponga de un sistema operativo residente.
Claramente, estos fueron los argumentos que mas pesaron sobre los diseadores de Ada y Java.
Los argumentos en contra de la concurrencia en el lenguaje son los siguientes:
(1) Cada lenguaje tiene un modelo de concurrencia distinto; resulta mas sencillo componer
programas de distintos lenguajes si todos utilizan el mismo modelo de concurrencia del
sistema operativo.
(2) Puede no ser fcil implementar eficientemente cierto modelo de concurrencia de un len-
guaje sobre algn modelo de sistema operativo.
(3) Comienzan a aparecer estndares de sistema operativo, y por tanto los programas se
vuelven mas portables.
7/31/2019 Capitulo 7. Programacion concurrente
4/9
4
La necesidad de soportar diversos lenguajes fue una de las principales razones por las que la
industria aeronutica civil, al desarrollar su programa de Avionica Modular Integrada, opto por una
interfaz de programacin de aplicaciones ncleo estndar (llamada APEX) que soporta concurrencia,
en vez de adoptar el modelo de concurrencia de Ada (ARINC AEE Committee, 1999). El debate, sin
duda, continuara durante algn tiempo.
7.1.1 Construcciones de programacin concurrente
A pesar de que las construcciones de programacin concurrente varan de un lenguaje (y sistemaoperativo) a otro, deben proporcionar tres servicios fundamentales, que se citan a continuacin;
La expresin de ejecucin concurrente mediante la nocin de proceso.
La sincronizacin de procesos.
La comunicacin entre procesos.
Al considerar la interaccin entre procesos, es til distinguir entre tres tipos de comportamiento:
Independiente
Cooperativo
Competitivo
Los procesos independientes no se comunican o sincronizan entre si. Por contra, los procesos
cooperativas se comunican con regularidad y sincronizan sus actividades para realizar alguna
operacin comn.
Un sistema informtico consta de un nmero finito de recursos que pueden ser compartidos entre
los procesos; por ejemplo, perifricos, memoria y potencia de procesador. Para que los procesos
obtengan su proporcin justa de recursos, deben compear entre si. La asignacin de recursos necesita
inevitablemente comunicacin y sincronizacin entre los procesos del sistema. Pero, aunque estos
procesos se comuniquen y sincronicen para obtener recursos, son esencialmente independientes.
7.2 Ejecucin concurrente.
A pesar de que la nocin de proceso es comn a todos los lenguajes de programacin concurrente,
hay variaciones considerables en los modelos de concurrencia que se adoptan. Estas variaciones
conciernen a los siguientes elementos:
Estructura
Nivel
Granularidad
Inicializacin
Finalizacin
Representacin.
La estructura de un proceso puede ser clasificada de la siguiente forma:
Esttica: el nmero de procesos es fijo y conocido en tiempo de compilacin.
Dinmica: los procesos son creados en cualquier momento. El nmero de procesos
existentes solo se determina en tiempo de ejecucin.
7/31/2019 Capitulo 7. Programacion concurrente
5/9
5
Otra distincin entre lenguajes proviene del nivel de paralelismo soportado. De nuevo, se pueden
identificar dos casos distintos:
Anidado: los procesos se definen en cualquier nivel del texto del programa; en particular,
se permite definir procesos dentro de otros procesos.
Piano: los procesos se definen nicamente en el nivel mas externo del texto del programa.
Dentro de los lenguajes que permiten construcciones anidadas, existe tambin una interesante
distincin entre lo que se puede llamar paralelismo de grano grueso y de grano fino. Un programa
concurrente de grano grueso contiene relativamente pocos procesos, cada uno con una historia devida significativa. Por su parte, los programas con paralelismo de grano fino tienen un numero mayorde procesos sencillos, algunos de los cuales existen para una nica accin. La mayora de los
lenguajes de programacin concurrentes, representados por Ada, muestran paralelismo de grano
grueso. Occam2 es un buen ejemplo de lenguaje concurrente con paralelismo de grano fino.
Cuando se crea un proceso, puede ser necesario proporcionar informacin relacionada con su
ejecucin (de la misma forma que un subprograma requiere cierta informacin cuando es invocado).Hay dos formas de realizar esta inicializacin: la primera es pasar al proceso la informacin en forma
de parmetros; la segunda es la comunicacin explicita con el proceso, despus de que haya
comenzado su ejecucin.
La finalizacin de procesos se puede realizar de distintas formas. A continuacin se resumen las
circunstancias en las que se permite que un proceso finalice:
(1) Finalizacin de la ejecucin del cuerpo del proceso.
(2) Suicidio por ejecucin de una sentencia de auto finalizacin.
(3) Aborto por medio de una accin explcita de otro proceso.
(4) Ocurrencia de una condicin de error sin tratar.
(5) Nunca; procesos que se ejecutan en bucles que no terminan.
(6) Cuando ya no son necesarios.
Con la anidacin en niveles, es posible crear jerarquas de procesos y establecer relaciones entre
ellos. Para cualquier proceso, es til distinguir entre el proceso (o bloque) que es responsable de su
creacin, y el proceso (o bloque) que es afectado por su finalizacin. La primera relacin es conocida
como padre / hijo, y posee la caracterstica de que el padre puede ser detenido mientras et hijo se creae inicializa. La segunda se denominada guardian/dependiente, y en el la un proceso puede depender
del propio proceso guardin o de un bloque interno de este. El guardin no puede terminar un bloque
hasta que todos los procesos dependientes hayan terminado (esto es, un proceso no puede existir fuera
de su alcance). Consecuentemente, un guardin no puede terminar hasta que lo hayan hecho todos sus
procesos dependientes. La consecuencia de esta regla es que un programa no podr terminar hasta que
todos los procesos creados en el hayan terminado tambin.
7/31/2019 Capitulo 7. Programacion concurrente
6/9
6
En algunas situaciones, el padre de un proceso Ser tambin su guardin. Este ser el caso cuando
se utilicen lenguajes que solo permiten estructuras estticas de procesos (por ejemplo occam2). Con
estructuras dinmicas de procesos (que tambin son anidadas), el padre y el guardin pueden no ser el
mismo. Esto se ilustrara posteriormente cuando se vea Ada.
Una de las formas en que un proceso puede finalizar punto (3) de la lista anterior es por la
aplicacin de una sentencia que lo aborta (abort). La existencia de abort en los lenguajes de pro-
gramacin concurrentes es una cuestin de cierta controversia. Para una jerarqua de procesos sueleser necesario que la interrupcin de un guardin implique la interrupcin de todos los dependientes (y
de los dependientes de estos, y as recursivamente).
La ultima circunstancia de finalizacin de la lista anterior se ver con mas detalle al describir los
mtodos de comunicacin de procesos. En esencia, permite que un proceso servidor termine si el resto
de procesos que podran comunicar con el ya han terminado.
7.2.1 Procesos y objetos
El paradigma de programacin orientada al objeto anima a que los constructores de sistemas (y de
programas) consideren el artefacto en construccin como un conjunto de objetos cooperantes o, parautilizar un termino mas neutral, de entidades. Bajo este paradigma, es provechoso considerar dos tipos
de objetos: activos y reactivos. Los objetos activos acometen acciones espontneamente (con unprocesador de por medio): hacen posible que la computacin prosiga. Los objetos reactivos, por
contra, solo entran en accin cuando son invocados por un objeto activo. Otros paradigmas de
programacin, como el de flujos de datos o el de redes de tiempo real, identifican agentes activos y
datos pasivos.
Solo las entidades activas dan lugar a acciones espontneas. Los recursos son reactivos, aunque
pueden controlar el acceso a su estado interno (y a cualquier recurso real que controlen). Algunos
recursos solo pueden ser usados por un nico agente en cada momento; en otros casos, las operaciones
realizables en un determinado momento dependen de los estados actuales de los recursos. Un ejemplo
comn del ultimo caso es el del buffer de datos, cuyos elementos no pueden ser extrados si esta vaco.
El termino pasivo se utilizara para designar entidades reactivas que permiten un acceso completo.
La implementacin de entidades de recurso requiere algn tipo de agente de control. Si el agente
de control es pasivo (como un semforo), entonces se dice que el recurso esta protegido (osincronizado). Por otro lado, si se precisa de un agente activo para programar el nivel adecuado decontrol, podemos decir, en cierto sentido, que el recurso es activo. El termino servidor se utilizara para
identificar a este ultimo tipo de entidades, y el termino recurso protegido para el tipo pasivo.
En un lenguaje de programacin concurrente, las entidades activas se representan medianteprocesos. Las entidades pasivas pueden representarse directamente como variables de datos, o pueden
ser encapsuladas por alguna construccin mdduio/paquete/clase que proporcione una interfaz
procedural. Los recursos protegidos tambin pueden estar encapsulados en una construccin tipo
modulo, y necesitar disponer de un servicio de sincronizacin de bajo nivel. Los servidores requieren
un proceso, ya que es necesario programar el agente de control.
Una cuestin clave para los diseadores de lenguajes es soportar o no primitivas para los recursos
protegidos y para los servidores. Los recursos suelen implementarse eficientemente (al menos en
sistemas uniprocesadores), ya que normalmente emplean un agente de control de bajo nivel (por
7/31/2019 Capitulo 7. Programacion concurrente
7/9
7
ejemplo un semforo). Pero, para algunas clases de programas, esto puede ser poco flexible, y llevar a
estructuras de programa deficientes (esto se discute con mas detalle en el Capitulo 8). Los servidores
son esencialmente flexibles, ya que el agente de control se programa mediante un proceso. El
inconveniente de esta aproximacin es que puede desembocar en un aumento de procesos y, en
consecuencia, en un elevado numero de cambios de contexto durante la ejecucin. Esto es
particularmente problemtico si el lenguaje no permite recursos protegidos y hay que utilizar un
servidor para cada cantidad.
7.3 Representacin de procesos
Hay tres mecanismos bsicos para representar la ejecucin concurrente; fork y join, cobegin y la
declaracin explicita de procesos. A veces se incluyen tambin las corrutinas como mecanismo para
expresar la ejecucin concurrente.
7.3.1 Corrutinas
Las corrutinas son como subrutinas, salvo que permiten el paso explicito de control entre ellas de
una forma simtrica en vez de estrictamente jerrquica. El control se transfiere de una corrutina a otra
mediante una sentencia reanuda (resume) que incluye el nombre de la corrutina con la que secontinua. Cuando una corrutina realiza una reanudacin, deja de ejecutarse, pero guarda
informacin del estado local, de forma que si, posteriormente, otra corrutina la hace reanudar, podr
retomar su ejecucin.
Cada corrutina puede verse como parte de un proceso separado; sin embargo, no es necesario un
sistema de soporte de ejecucin, ya que las propias corrutinas se colocan en orden de ejecucin.
Claramente, las corrutinas no son adecuadas para un autentico procesamiento paralelo, ya que su
semntica solo permite la ejecucin de una rutina cada vez. Modula-2 es un ejemplo de lenguaje que
soporta corrutinas.
7.3.2 Fork y join
Esta sencilla aproximacin no proporciona una entidad visible de proceso, sino que aporta dos
instrucciones. La instruccin fork (bifurca) indica que cierta rutina deber comenzar a ejecutarse
concurrentemente con quien ha invocado fork. La instruccin join (rene) permite al que invoca
detenerse, y por ende sincronizarse, hasta la terminacin de la rutina invocada. Por ejemplo:
7/31/2019 Capitulo 7. Programacion concurrente
8/9
8
El lenguaje Mesa proporciona la notacin fork y join. tambin POSIX proporciona una versin de
fork y join; aunque aqu fork sirve para crear una copia del invocador, y el join efectivo se obtiene
mediante la llamada del sistema wait.
Fork y join permiten crear procesos dinmicamente, y proporcionan un mecanismo para pasar
informacin al proceso hijo a travs de parmetros. Normalmente, al terminar el hijo, devuelve un
solo valor. Aunque flexibles, fork y join no proporcionan una aproximacin estructurada a la creacinde procesos, y su utilizacin es propensa a errores. Por ejemplo, un proceso guardin debe reunir
explcitamente todos los procesos dependientes, en vez de esperar simplemente a que terminen.
7.3.3 Cobegin
Cobegin (o par) es una forma estructurada de denotar la ejecucin concurrente de un conjunto de
instrucciones:
Cobegin
S1;
S2;
Sn;
coend
Este cdigo permite la ejecucin concurrente de las instrucciones SI, S2, etc. La instruccin
cobegin (comienzo) termina cuando han terminado todas las instrucciones concurrentes. Cada
instruccin Si puede ser cualquiera de las construcciones permitidas en el lenguaje, incluyendo lasasignaciones sencillas o las llamadas a procedimientos. De invocar algn procedimiento, podrn
pasarse datos a los procesos invocados mediante los parmetros de la llamada. La instruccin cobegin
podra incluir, a su vez, una secuencia de instrucciones donde apareciera cobegin, y as construir una
jerarqua de procesos.
Cobegin puede encontrarse en occam2.
7.3.4 Declaracin explicita de procesos
A pesar de que cobegin y fork permiten expresar la ejecucin concurrente de rutinas secuenciales,
la estructura de un programa concurrente es mucho mas ntida si las propias rutinas son quienesestablecen su ejecucin concurrente. La declaracin explicita de procesos proporciona esta
posibilidad.
Otros lenguajes que soportan la declaracin explcita de procesos, como por ejemplo Ada,
permiten tambin la creacin implcita de tareas. Todos los procesos declarados dentro de un bloque
comienzan a ejecutarse concurrentemente al final de la parte declarativa de dicho bloque.
En la anterior discusin se han resumido los modelos bsicos de ejecucin concurrente, y se ha
hecho referencia a lenguajes que soportan ciertas caractersticas particulares. Con el fin de
7/31/2019 Capitulo 7. Programacion concurrente
9/9
9
proporcionar ejemplos concretos sobre lenguajes de programacin reales, se vera la ejecucin
concurrente en occam2, Ada y Java. Complementariamente, se comentara la ejecucin concurrente en
POSIX,
Top Related