Universidad Nacional de Educación a Distancia Guı́a Didáctica Programación Concurrente (Ingenierı́a Técnica Informática de Sistemas) Equipo docente: David Fernández Amorós . c 2008 David Fernández Amorós. Todos los derechos reservados. 2 ÍNDICE Índice 1. Información general 5 1.1. Presentación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.2. Introducción a la asignatura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 2. Presentación de los contenidos 2.1. Objetivos generales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 8 3. Requisitos previos 8 4. Materiales 9 5. Orientaciones generales para el estudio 10 6. Distribución del tiempo de estudio 12 7. Evaluación 12 7.1. Pruebas de evaluación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 7.2. Criterios de calificación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 8. Programa 13 8.1. Bloque temático 1: Introducción y conceptos básicos . . . . . . . . . . . . . . . . . . 13 8.1.1. Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 8.1.2. Orientaciones especı́ficas de los temas . . . . . . . . . . . . . . . . . . . . . . 15 8.2. Bloque temático 2: Herramientas para manejar la concurrencia . . . . . . . . . . . . . 17 8.2.1. Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 8.3. Orientaciones especı́ficas de los temas . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3 ÍNDICE 8.4. Bloque temático 3: Problemas de aplicación . . . . . . . . . . . . . . . . . . . . . . . 23 8.4.1. Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 8.5. Orientaciones sobre los contenidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 9. Actividades 24 4 1 Información general 1. 1.1. Información general Presentación Esta guı́a pretende orientar al alumno en la planificación y el estudio de la asignatura. Se recomienda leer la guı́a en su totalidad al principio del cuatrimestre para tener una visión de conjunto de la dinámica del curso. Esta información se complementa con el enunciado de la práctica obligatoria, las preguntas frecuentes y los problemas de examen disponibles en el curso virtual, de forma que el estudiante pueda planificar desde el principio el estudio y realización de la práctica para aprovechar el tiempo al máximo y no perder los plazos de entrega. 1.2. Introducción a la asignatura El ámbito de conocimiento de esta asignatura es el de la programación en computación paralela o distribuida. La programación concurrente (en adelante PC) añade una capa suplementaria de complejidad respecto de la secuencial, por cuanto que es una extensión de esta, en el sentido de que un programa concurrente está compuesto por varios programas secuenciales que pueden ejecutarse simultaneamente, compartiendo el acceso a unos recursos compartidos, con vistas a la consecución de un objetivo común. De la compartición de recursos surge la necesidad de arbitrar mecanismos adecuados para permitir el acceso, ya sea para leer, ya sea para modificar el recurso compartido, de manera que se garantice que la información que proporciona en cualquier momento sea coherente. Esta necesidad de coherencia, unida a la articulación de los programas individuales que constituyen el programa concurrente en un cálculo común, lleva a la cuestión de la sincronización entre los programas. Se estudiarán diferentes herramientas propuestas históricamente para manejar la ejecución simultanea de programas junto con esquemas algorı́tmicos desarrollados para el tratamiento de situaciones comunes en PC. Las técnicas aprendidas se pondrán en aplicación en un trabajo práctico de carácter obligatorio en el que se desarrollará un programa concurrente que sirva de modelo a una situación descrita en el enunciado del mismo. Los contenidos de esta asignatura se encuentran incluidos en otros planes de estudio dentro las asignaturas de sistemas operativos o de sistemas en tiempo real, si bien comúnmente en dichas materias la problemática especı́fica de la PC no se encuentra desarrollada, ni en extensión ni en profundidad al nivel en que se hace en esta. En el marco docente que desarrolla el actual plan de estudios, PC es una asignatura optativa de tercer curso de la titulación de Ingenierı́a Técnica en Informática de Sistemas, si bien está disponible como asignatura de libre configuración y de libre elección dentro de la Escuela 5 1.2 Introducción a la asignatura Superior de Ingenierı́a Informática de la UNED. Se imparte durante el primer cuatrimestre del tercer curso. La evaluación de la asignatura incluye un trabajo obligatorio de ı́ndole práctica por imperativo del reglamento del departamento LSI. En la práctica obligatoria de la asignatura y en el examen sólo se exigirá la codificación de los programas en pseudocódigo, sin embargo, se recomienda encarecidamente que el programa de la práctica y los ejercicios se codifiquen y prueben con un lenguaje de programación concreto. En la bibliografı́a de la asignatura se hace hincapié principalmente en los lenguajes PASCAL-FC, Java y en menor medida Ada, aunque el lenguaje elegido no es importante siempre que se utilicen las herramientas de sincronización pertenecientes al temario, aunque sea de forma simulada. Cada uno de ellos tiene sus ventajas e inconvenientes: PASCAL-FC es el decano de los lenguajes de programación concurrente, no tanto por su antigüedad sino por el hecho de no haber sido actualizado desde hace décadas. Con implementaciones para DOS, Windows y Linux, sus ventajas son la simplicidad del lenguaje, que tiene las mı́nimas caracterı́sticas de programación, y la variedad de herramientas de concurrencia que soporta. Existen editores especı́ficos para Windows e incluso un plugin para el entorno de programación multiplataforma ECLIPSE. Entre las desventajas, se podrı́a mencionar que tiene un cuarto de siglo con todo lo que ello conlleva, restricciones absurdas hoy en dı́a respecto al número máximo de procesos y de monitores, falta de tipos de datos básicos como serı́an las cadenas de caracteres (sólo existe el tipo carácter) y hasta máximo número de instrucciones ejecutadas. Un programa que ejecute 200000 instrucciones es abortado y automáticamente sospechoso de livelock. Tampoco hay manera de definir una función dentro de un monitor, aunque teóricamente es posible. Estas limitaciones pueden ser severas incluso a la hora de realizar la práctica de la asignatura. Java es un lenguaje de propósito general, multiplataforma y constantemente modernizado. Existen editores y documentación de todo tipo. Si bien las limitaciones de sus implementaciones en diversas plataformas y el hecho de ser muy a menudo interpretado pueden afectar negativamente sus posibilidades reales, sus limitaciones están a años luz de las de PASCAL-FC. Aunque sus primitivas de sincronización inicialmente no tenı́an nada que ver con las estudiadas en la asignatura, hoy en dı́a la situación ha mejorado considerablemente. El punto más débil de Java para esta asignatura es que requiere el aprendizaje de un lenguaje complejo, mientras que PASCAL-FC es casi inmediato. Ada es un lenguaje de propósito general, multiplataforma. Se considera un lenguaje especializado. Entre sus principios de diseño se encuentran la eficiencia y la concurrencia, cosa que no puede decirse a la vez de los dos lenguajes anteriormente mencionados. Sus primitivas de concurrencia apenas coinciden con las del temario de la asignatura, excepto en el caso de la invocación remota (también conocida como cita de Ada o rendez-vous). Las desventajas son claras: se trata de un lenguaje de una extraordinaria complejidad, 6 2 Presentación de los contenidos con una curva de aprendizaje alta, poca variedad de compiladores y pocas posibilidades de uso fuera del departamento de defensa de los Estados Unidos de América, donde fue desarrollado originalmente. La PC se ha restringido tradicionalmente a unas áreas de aplicación muy concretas, pero de una importancia incuestionable: Sistemas empotrados: Sistemas sencillos, habitualmente sin sistema operativo, en un hardware especı́fico, en los que unos programas que se ejecutan concurrentemente leen unos sensores y toman decisiones como activar o desactivar otros componentes electrónicos. Un ejemplo sencillo serı́a un termostato. Sistemas operativos: En un sistema operativo multiprogramado lo normal es que haya varios procesos ejecutándose en paralelo (ya se trate de paralelismo real o simulado). Es necesario sincronizar dichos procesos para que los programas del sistema y los de los usuarios puedan compartir los recursos de la máquina. Un caso particular pero especialmente relevante serı́a el de los sistemas operativos distribuidos. Sistemas de Gestión de Bases de Datos: Los SS.GG.BB.DD tienen entre sus objetivos de diseño el proporcionar un acceso eficiente a los datos manteniendo la consistencia de los mismos. Para permitir el acceso simultaneo de varios programas y mantener al mismo tiempo la consistencia de los datos entre las transacciones, se aplican técnicas de PC. Computación en malla: Se utilizan muchos ordenadores, conectados en red, para realizar un cálculo entre todos. El ejemplo más conocido quizá sea el del proyecto SETI@Home, en el que ordenadores situados a lo largo de todo el planeta buscan coordinadamente indicios de vida extraterrestre. Pese a sus comienzos especializados, en la informática de consumo se ha popularizado en los últimos tiempos el uso de sistemas multiprocesador. En estas CPU’s con dos, tres o cuatro núcleos, las ventajas de la PC son incluso más claras que en un entorno monoprocesador, lo que hace previsible un aumento de la demanda de profesionales con conocimientos sólidos en este campo. 2. Presentación de los contenidos Tras presentar algunos conceptos fundamentales de la PC, el foco se desplaza al uso de técnicas concretas de PC. En particular, se hace un recorrido por algunas de las herramientas 7 2.1 Objetivos generales de sincronización de procesos concurrentes en el ámbito de la memoria compartida: semáforos, regiones crı́ticas, regiones crı́ticas condicionales y monitores. En los temas siguientes se introducen los conceptos asociados al paso de mensajes y algunas de las herramientas correspondientes, en concreto buzones e invocación remota. En todos los temas referentes a herramientas concretas se estudian soluciones a los dos problemas-tipo de la PC, que pueden verse como esquemas algorı́tmicos: El problema de los lectores y escritores y el problema de los productores y consumidores. Otro aspecto transversal es el de la equivalencia de las distintas herramientas; el comportamiento de cualquier herramienta puede simularse mediante otra, a pesar de lo cual todas ellas poseen caracterı́sticas distintivas que hacen que unas sean más apropiadas que otras frente a un problema concreto. 2.1. Objetivos generales Presentar los problemas derivados de la ejecución simultanea de tareas Introducir esquemas algorı́tmicos generales aplicables a numerosos problemas Adiestrar al estudiante en el empleo de diversas herramientas de sincronización de procesos Desarrollar la capacidad de análisis y diseño de soluciones a un problema de simulación mediante el uso de técnicas aprendidas en la asignatura 3. Requisitos previos En un modelo de capas o niveles de abstracción, la PC se encontrarı́a en un nivel superior al de la programación convencional. Dado que es el sistema el que proporciona la concurrencia, lo que corresponde al programador es administrar dicha concurrencia, de forma que las diferentes partes de un programa concurrente calculen juntas un resultado al tiempo que se mantiene la consistencia de los datos. Las técnicas de programación secuencial se dan por supuestas. Sin embargo, dicho esto hay que matizar que el énfasis de la materia se centra en los mecanismos de sincronización de procesos, por lo que no se presta demasiada atención a los aspectos formales y metodológicos de la programación. Por tal motivo, los únicos requisitos previos serı́an un conocimiento sólido de los contenidos correspondientes a la asignatura Programación I, de primer curso. Serı́a útil, aunque no imprescindible, que el alumno tuviera una cierta familiaridad con con los contenidos de la asignatura Estructuras de Datos y Algoritmos, de segundo curso, especialmente en lo referido a tipos abstractos de datos. Por último, el contenido de la asignatura se relaciona con el de Sistemas Operativos I, de segundo curso, por lo que respecta a la relación de los estados de un programa concurrente en relación con el sistema operativo, por lo que una cierta familiaridad con los conceptos fundamentales de este también 8 4 Materiales resulta conveniente. Con esta asignatura hay un cierto grado de solapamiento, puesto que en ella se dedica un tema de estudio a la PC. Evidentemente, el enfoque de aquella resulta más ligero, por lo que su estudio podrı́a proporcionar una introducción a los temas desarrollados en esta asignatura. Dejando de lado algunas cuestiones de estilo de programación, los problemas habituales de la asignatura no suelen estar relacionados con un aprendizaje deficiente de asignaturas anteriores, por lo que no se considera necesario reforzar capacidades anteriores. Se recomienda que se implementen y se prueben los problemas de los ejercicios y de la práctica obligatoria, por lo que se asume cierta destreza en la instalación y utilización de entornos de compilación y desarrollo. 4. Materiales Los contenidos de la asignatura se desarrollan completamente en el texto base: Programación Concurrente, Palma et al., Editorial Thomson, 2003. En el texto base se discuten los conceptos fundamentales de la asignatura, ası́ como numerosos ejemplos de aplicación y ejercicios resueltos. También se proponen problemas por resolver, de modo que resulta un texto adecuado para el aprendizaje a distancia. Es cierto que en algunas de sus consideraciones prácticas, como la implementación en Java de algunas de las técnicas, ya está obsoleto debido al rápido avance de la técnica, pero afortunadamente, la presentación de los conceptos y técnicas es completamente actual. El alumno encontrará en el curso virtual materiales adicionales para el seguimiento de la asignatura. Entre ellos, enunciados de exámenes anteriores, exámenes resueltos, demostraciones de simulaciones realizadas aplicando las técnicas objeto de estudio, ası́ como compiladores y documentación sobre lenguajes de programación con posibilidades de concurrencia. Como resultado de un seguimiento de las dificultades de los alumnos en relación con la asignatura, se ha realizado un lista de preguntas frecuentes para optimizar el proceso de aprendizaje y evitar la sobrecarga del equipo docente, que redunda en unos tiempos de respuesta más largos a las dudas del alumnado. Una parte del mismo material se ha incluido en el DVD de la Escuela Superior de Ingenierı́a Informática, para aquellos alumnos a los que les resulte más conveniente este medio. La bibliografı́a complementaria consta de las siguientes obras: Programación concurrente. 2a edición corregida, Pérez Martı́nez, J. E., Madrid, Editorial Rueda, 1990. 9 5 Orientaciones generales para el estudio Durante algunos años, esta obra fue el texto base de la asignatura. Esta obra peca quizás de un enfoque un tanto teórico para una disciplina aplicada como es la programación, pero supone un excelente contrapunto al estudio del texto base, ya que, a menudo, la bibliografı́a básica menciona rápidamente aspectos vitales, como pueden ser la necesidad absoluta de la exclusión mutua en los accesos no compatibles, o las causas de los interbloqueos y las polı́ticas para evitarlo, pero no los subraya. Este libro profundiza teóricamente y ejemplifica los conceptos. El análisis de los puntos fuertes y débiles de cada una estas obras en relación con la otra permite obtener una visión de conjunto de la disciplina mucho más completa, y altamente recomendable, que la proporcionada por cada una de ellas individualmente. Principles of Concurrent and Distributed Programming. 2o Edición, Ben-Ari, M., AddisonWesley, 2006 Mordechai Ben-Ari es una de las figuras de referencia de la PC actual. Este matemático israelı́, premiado por sus contribuciones a la enseñanza de la informática ha sido un activo colaborador en el desarrollo del lenguaje Ada. Una prueba de lo influyente de esta obra, desde la primera edición en 1990, la da la mera comparación de su ı́ndice con el de las dos obras citadas anteriormente. Ada for Software Engineers, Ben-Ari, M., John Wiley & Sons, 1998. Este libro proporciona una introducción al lenguaje Ada de la mano de unos los expertos en el campo de la computación distribuida. Las caracterı́sticas concurrentes forman parte de los principios de diseño de Ada, hecho este que lo diferencia de la mayorı́a de los lenguajes de programación con posibilidades de concurrencia, en las que estas no suponen más que un añadido a posteriori. 5. Orientaciones generales para el estudio Una primera lectura rápida del libro puede proporcionar una adecuada visión de conjunto de los problemas que se van a tratar. En esta primera lectura se pueden ignorar los apartados relativos a la puesta en práctica de la teorı́a en los diversos lenguajes considerados (PASCALFC, Java y Ada). La primera unidad didáctica se corresponde con parte de los tres primeros capı́tulos del texto base, que pueden considerarse como una introducción extendida a los conceptos fundamentales de la PC. La información relevante de estos capı́tulos puede ser extraı́da en la primera lectura, de forma que no se precise una relectura más adelante. La segunda unidad didáctica trata las diversas herramientas para manejar la concurrencia. La tercera consiste en las soluciones a problemas de aplicación. Estas dos unidades están entremezcladas en el texto. Esta parte del libro puede estudiarse siguiendo el esquema de distribución del tiempo de la siguiente sección, o bien leerse en orden ya que, después de 10 5 Orientaciones generales para el estudio presentar una herramienta para controlar la concurrencia, se pasa a implementar la solución a los problemas-tipo utilizando dicha herramienta. Este enfoque ofrece la ventaja de que el estudiante se familiariza rápidamente con los problemas que se resuelven y pronto sólo le resulta novedosa la solución que emplea la herramienta especı́fica considerada. Estas dos unidades didácticas se merecen una segunda lectura más interpretativa. En primer lugar, para ser capaz de programar correctamente una solución a un problema concreto, es necesario dominar los detalles de cada herramienta. Cada herramienta consta de una sintaxis, semántica y conjunto de estados especı́fico para los procesos (con sus diversos circuitos de colas de espera asociadas) que deben conocerse y manejarse con soltura. Un ejercicio muy útil para aclarar dudas y retener conceptos es implementar las primitivas de una herramienta utilizando otra. Este ejercicio tiene además la ventaja de que a menudo un lenguaje de PC no soporta de forma nativa más que unas pocas herramientas de sincronización, de forma que si queremos usar otras deberemos primero simularlas mediante esas herramientas nativas. Otro aspecto sobre el que conviene reflexionar a medida que se estudia el texto base, es el comparar unas herramientas de sincronización con otras. No hay herramientas mejores que otras, puesto que a nivel teórico todas son equivalentes, pero sı́ se pueden clasificar según sean de alto o bajo nivel, sirvan para cualquier arquitectura, caso de las de paso de mensajes, o sólo sean aptas para entornos de memoria compartida, etc. . . En una situación concreta, es muy posible que haya unas herramientas más adecuadas que otras, por ello hay que intentar ser consciente de las diferencias entre ellas. En cualquier caso, tras esa primera lectura es cuando se puede comenzar a trabajar la práctica obligatoria de la asignatura, aunque este trabajo se limite a poco más que el análisis del enunciado. Es por desgracia frecuente que un alumno, presionado por los plazos de entrega, decida enfocar la asignatura con el método de tirarse a la piscina, o sea, empezar a programar sin leerse el libro apenas, o sin haberlo leı́do en absoluto. Habida cuenta de la abundancia de material sobre programación disponible en la Red, esta posibilidad supone una tentación, sin embargo dicho enfoque suele dejar unas lagunas que terminan suponiendo un grave lastre, que es detectado por el equipo docente más tarde de lo que serı́a deseable (tı́picamente después de la primera entrega de la práctica, lo que lleva a hacer una segunda entrega no deseada previamente por ninguna de las partes). Una aproximación más interesante puede ser la siguiente; después de hacer una lectura rápida de los temas del libro correspondientes al temario, se puede analizar el enunciado de la práctica y tomar una decisión sobre la herramienta que se va a utilizar en la práctica obligatoria. Resulta vital analizar si el problema de la práctica se puede asimilar de alguna manera a algún problema-tipo. Como sólo hay dos problemas-tipo, la comprobación no deberı́a ser difı́cil. A continuación, se puede estudiar detenidamente dicha herramienta, de modo que el trabajo sobre la práctica pueda ser llevado paralelamente al estudio del resto del temario. 11 6 Distribución del tiempo de estudio Unidad Didáctica I II III Temario Horas de estudio Teorı́a Práctica Tema 1. Introducción 2 Tema 2. Procesos vs. hilos 1 Tema 3. Exclusión mutua 2 Tema 4. Semáforos 3 Tema 5. Regiones Crı́ticas Condicionales 2 Tema 6. Monitores 3 20 Tema 7. Buzones 3 Tema 8. Invocación Remota 3 Tema 9. Equivalencia de Herramientas 3 Tema 10. Problemas de aplicación 5 Figura 1: Temporización de la asignatura 6. Distribución del tiempo de estudio Programación concurrente es una asignatura cuatrimestral de cinco créditos: tres de teorı́a y dos de práctica. Un crédito equivale a diez horas de estudio, lo que resulta en cincuenta horas de estudio. En la figura 6 se puede ver la temporización propuesta. Se propone un plan de trabajo de unas dos horas y media de estudio teórico por semana. A partir de la tercera semana, el estudio teórico se combina con la realización de la práctica a razón de unas dos horas por semana. 7. 7.1. Evaluación Pruebas de evaluación La calificación de la asignatura tendrá en cuenta la calificación de la práctica obligatoria de la asignatura y la calificación de la prueba presencial. Para aprobar la asignatura, tanto la práctica como la prueba presencial deben estar aprobadas. La nota de la práctica en una convocatoria se guarda para las siguientes del mismo curso. La práctica sólo tiene dos calificaciones: apto y no apto. La superación de la práctica no implica aumento de la calificación final, aunque en algunos casos excepcionales puede aumentar o disminuir ligeramente la calificación total. En el SIRA sólo quedará reflejada la calificación global, aunque en el curso virtual aparecerá detallada la calificación de la práctica. No hay pruebas de evaluación a distancia. La práctica de la asignatura no comporta la asistencia a sesiones presenciales. Los alumnos sin 12 7.2 Criterios de calificación tutor pueden consultar sus dudas sobre la práctica en el foro correspondiente del curso virtual. La no superación de la práctica en una convocatoria conlleva el suspenso en dicha convocatoria, por lo que si las nota de la práctica está disponible antes de los exámenes y no ha sido superada, no tiene sentido realizar la prueba presencial. 7.2. Criterios de calificación En la prueba presencial se pedirá el desarrollo de un programa concurrente similar al de la práctica. El programa del examen requiere la puesta en práctica de los conocimientos adquiridos durante el estudio de la asignatura, pero habitualmente no suele consistir en una aplicación inmediata de un concepto teórico o problema-tipo. Es frecuente que se mezclen los problemastipo de alguna forma, e incluso que sea necesario generalizar y combinar los problemas-tipo de forma no trivial y con cierto grado de creatividad. También ocurre con frecuencia que el programa del examen no responda claramente a un esquema algorı́tmico. En función de la complejidad del mismo, puede haber también cuestiones teórico-prácticas. Los criterios de evaluación de la práctica y del problema de la prueba presencial son similares. 8. Programa La asignatura se articula a través de tres bloques temáticos: Bloque temático 1: Introducción y conceptos básicos Bloque temático 2: Herramientas para manejar la concurrencia Bloque temático 3: Problemas de aplicación Estos bloque temáticos se describen en detalle a continuación. 8.1. Bloque temático 1: Introducción y conceptos básicos Este bloque temático presenta las particularidades suplementarias de la PC respecto a la programación secuencial. Se motiva la necesidad y conveniencia de la PC, y se realizan algunas consideraciones sencillas sobre su implementación a bajo nivel. También se introducen los conceptos de proceso e hilo, lo que permite profundizar en la problemática de la PC en general y de la exclusión mutua en particular. 13 8.1 Bloque temático 1: Introducción y conceptos básicos 8.1.1. Objetivos Tras estudiar este bloque temático, el estudiante deberá haber comprendido las diferencias esenciales entre la PC y la secuencial, ası́ como ser consciente de las diferentes arquitecturas fı́sicas de los sistemas en los que puede emplearse la PC. Una de estas caracterı́sticas añadidas es el concepto de estado del de un proceso. En programación clásica los estados de un programa son limitados; el sistema operativo carga y lanza el programa, que se ejecuta hasta su finalización. En PC cada proceso o hilo es un correlato del programa secuencial, pero puede encontrarse en un conjunto de estados mayor. Estos estados adicionales se derivan del hecho de que los procesos tienen que sincronizarse entre ellos, lo que a menudo significa que algunos de ellos deben esperar a que otros hayan realizado parte de su tarea. El otro gran problema de la PC es el de la exclusión mutua. Dos procesos no deben acceder concurrente a un mismo recurso (como una variable compartida o un periférico) de forma no compatible. Una vez que un programa concurrente se sincroniza en sus diversas partes correctamente y preserva la exclusión mutua a los recursos no compartibles, se puede considerar correcto. Sin embargo, hay otros problemas graves que pueden hacer poco aceptable un programa concurrente correcto. Algunos de estos problemas son la inanición, el establecimiento de turnos, la baja concurrencia, la espera activa y el interbloqueo. El interbloqueo se trata extensamente en el libro de Pérez Garcı́a, donde se discuten técnicas diferenciadas para detectarlo, evitarlo y prevenirlo. El estudiante deberá ser capaz de identificar estas situaciones para poder evitarlas empleando las herramientas descritas en el segundo bloque temático. Temas 1. INTRODUCCION Conceptos fundamentales de la programación concurrente Concepto de programación concurrente Beneficios de la programación concurrente Concurrencia y arquitecturas hardware Especificación de ejecución concurrente Caracterı́sticas de los sistemas concurrentes Problemas inherentes a la programación concurrente 2. PROCESOS vs. HILOS Procesos Hilos 14 8.1 Bloque temático 1: Introducción y conceptos básicos Grafos de estados de un proceso / hilo Ciclo de vida de un proceso / hilo Grafos y diagramas de precedencia 3. EXCLUSION MUTUA. Introducción al problema Tipos de sincronización y su solución Soluciones con espera ocupada (excepto algoritmos de Eisenberg-McGuire y Algoritmo de Lamport) 8.1.2. Orientaciones especı́ficas de los temas Tema 1 Introducción La introducción a la Programación Concurrente, como toda la asignatura, puede estudiarse por el libro de Palma et al., en adelante texto base. La secciones que proporcionan una introducción a los conceptos fundamentales de la PC, son la 1.1 (introducción) junto con la 1.2 (concepto de PC), 1.3 (beneficios de la PC), 1.4 (concurrencia e arquitecturas hardware), 1.5 (especificación de ejecución concurrente), 1.6 (caracterı́sticas de los sistemas concurrentes), 1.7 (problemas inherentes a la PC) y 1.8 (corrección de programas concurrentes). El estudio de este tema debe proporcionar al estudiante una visión de conjunto de la problemática de la PC y de algunas caracterı́sticas diferenciales respecto a la programación secuencial. En particular, las diferentes arquitecturas hardware posibles, el concepto de traza de ejecución y el indeterminismo tienen una influencia determinante en PC. Junto a los beneficios presentados de la PC, hay que ser consciente de los problemas asociados, que pueden ser de diversos tipos. Lo más importante que hay retener de este tema es que un programa concurrente no debe hacer ninguna suposición sobre la arquitectura del sistema en el que se ejecuta. Un programa concurrente se puede ejecutar en un entorno monoprocesador multiprogramado, en el cual no se produce concurrencia real sino que se simula. Sin embargo, también es frecuente (quizá lo más frecuente en nuestros dı́as) que la máquina cuente con varios procesadores, de manera que, de hecho, haya varios procesos ejecutándose en el mismo momento. El programador, en principio, sólo deberı́a preocuparse de saber si la arquitectura admite memoria compartida o, por el contrario, se trata de un sistema distribuido. En el primer caso, todas las primitivas de sincronización pueden ser consideradas, en el segundo, sólo las herramientas basadas en paso de mensaje son admisibles. 15 8.1 Bloque temático 1: Introducción y conceptos básicos Las condiciones de Bernstein pueden ayudar a determinar qué es lo que se puede ejecutar de forma concurrente y lo que no, aunque como resumen, se puede decir que cualquier número de procesos puede leer de un determinado recurso de forma concurrente. Sin embargo, el que haya un proceso escribiendo en el recurso lo hace incompatible tanto con otros procesos escritores como con procesos lectores. Esta cuestión está estrechamente relacionada con la del indeterminismo. Si bien el indeterminismo en programación no es negativo per se (imaginemos, por ejemplo, un generador de números pseudoaleatorios) un programa concurrente querrá en la mayorı́a de los casos mantener cierta coherencia en los datos. En el ejemplo de la subsección 1.6.2, vemos como un acceso no controlado a una variable hace que el valor de la misma sea impredecible. Pensemos ahora en un programa que controle el funcionamiento de una red de cajeros automáticos. Podemos querer cierta concurrencia, pero seguramente no queremos que se cree ni se destruya dinero en el proceso. La forma correcta de enfocar el problema tiene que garantizar el acceso en exclusión mutua a las cuentas en los accesos incompatibles con las condiciones de Bernstein. Esta cuestión es lo suficiente importante como para repetirla una vez más: Una variable compartida debe ser accedida siempre en exclusión mutua, salvo en los casos en los que se asegure que no hay ningún otro proceso intentando modificar dicha variable al mismo tiempo. En la práctica, esto significa que las variables compartidas deben accedidas (ya sea para leer o para modificar sus valores) en exclusión mutua, salvo que se desarrolle un esquema de lectores y escritores, objeto de estudio del tercer bloque temático. Tema 2 Procesos vs. hilos En el segundo capı́tulo del libro de Palma et al. se profundiza en las diferencias entre procesos e hilos en las secciones 2.1 (procesos) y 2.3 (hilos) Los conceptos más importantes de este tema son: • La distinción entre programa, proceso e hilo. • El concepto de estados de un proceso o hilo y su ciclo de vida. • Los grafos y diagramas de precedencia. • Los problemas de la PC, en particular la exclusión mutua y la condición de sincronización. Excepción hecha de las dos secciones mencionadas, el resto de este capı́tulo del libro no está muy relacionado con el contenido de la asignatura. Contiene multitud de detalles técnicos que son a la vez demasiado avanzados y relativamente irrelevantes a la hora de comprender conceptualmente la asignatura. En algún caso la información allı́ expuesta puede servir para el estudiante a la hora de codificar y ejecutar sus programas concurrentes. Tema 3 Exclusión mutua 16 8.2 Bloque temático 2: Herramientas para manejar la concurrencia En la sección 3.1 se hace una introducción al problema. En la 3.2 (tipos de sincronización y su solución) se plantea el problema de la exclusión mutua y su división entre soluciones con espera activa y sin espera activa. También se plantea el problema de la condición de sincronización. En la sección 3.3 (soluciones con espera ocupada) excepto 3.3.5 (algoritmo de Eisenberg-McGuire) y 3.3.6 (algoritmo de Lamport) se estudian varios algoritmos, algunos de ellos incorrectos, otros correctos pero con problemas inaceptables y finalmente algunas soluciones aceptables desde el punto de vista teórico basadas en espera activa. Al terminar de estudiar este tema, los conocimientos mı́nimos que se deben haber adquirido son: • Las diferencias principales entre las soluciones con espera activa y sin esperada activa. • Ser capaz de distinguir una solución con espera activa correcta al problema de la exclusión mutua de una incorrecta. También distinguir, entre las correctas, las aceptables de las inaceptables y poder explicar por qué (alternancia, espera infinita,. . . ). Las soluciones hardware explicadas en el texto base quedan fuera del ámbito de la asignatura. Lo único que el estudiante requiere saber es que todas las soluciones software al problema de la exclusión mutua son de una ineficiencia tal que resulta evidente que la exclusión mutua debe implementarse a nivel de hardware. La forma concreta de estas soluciones hardware de bajo nivel es irrelevante para nosotros, que nos centraremos en cómo aprovechar las posibilidades para la exclusión mutua de las herramientas para manejar la concurrencia que son el objeto del siguiente bloque temático. 8.2. Bloque temático 2: Herramientas para manejar la concurrencia En este bloque se discuten diversos conjuntos de herramientas que se han propuesto teóricamente para manejar la concurrencia. Dichas herramientas están implementadas en proporción desigual en los lenguajes de programación. Mientras algunas de ellas, como los semáforos, han sido aceptadas de forma casi universal, otras han retenido su condición de propuestas más teóricas. Es digno de mención sin embargo, que otras han superado este status en fechas relativamente recientes, como es el caso de la programación orientada a sucesos o eventos, popularizada con el uso de las interfaces gráficas de usuario y la programación orientada a objetos. El proceso inverso, conjuntos de herramientas diferentes de los clásicos, pero implementados por los lenguajes de uso frecuente, como podrı́a ser el objeto protegido de Ada y las primitivas wait y notify de Java, también son frecuentes. En este bloque temático se estudia también como superar esta separación entre propuestas teóricas y lo que de verdad está disponible a la hora de programar. La PC es un campo que evoluciona rápidamente, por lo que el estudiante comprobará que es difı́cil establecer cuáles son dichas diferencias. El caso de 17 8.2 Bloque temático 2: Herramientas para manejar la concurrencia Java es paradigmático en este sentido, ya que implementa de forma nativa primitivas de concurrencia no disponibles anteriormente en dicho lenguaje a una velocidad sorprendente, que convierte en obsoletas algunas de las recomendaciones del texto base. Todas las herramientas para manejar la concurrencia tienen la misma potencia y expresividad. No hay nada que pueda hacerse con una de ellas que no pueda hacerse con otra. Sin embargo, los distintos planteamientos pueden producir resultados muy diferentes en cada problema concreto. La elección de una de ellas para un problema concreto puede venir forzada por el planteamiento o por el lenguaje, ser una cuestión de preferencias personales o el resultado de un compromiso entre elegancia y eficiencia. 8.2.1. Objetivos El estudiante deberá familiarizarse con estas herramientas y ser consciente de sus diferencias. Las caracterı́sticas de una herramienta concreta, o la simulación de una de ellas mediante la otra suelen ser objeto de evaluación por parte del equipo docente. Temas 4. SEMÁFOROS Introducción Definición de un semáforo Inconvenientes de los semáforos 5. REGIONES CRÍTICAS CONDICIONALES (RCC) Introducción Definición de RCC Inconvenientes de las RCC 6. MONITORES Introducción Definición de monitor Condición de sincronización Semántica de la operación resume 7. BUZONES 18 8.3 Orientaciones especı́ficas de los temas Introducción Identificación en el proceso de comunicación Sincronización Canal de comunicación y mensajes Espera selectiva Funcionamiento de los buzones 8. INVOCACIÓN REMOTA Introducción Funcionamiento de la invocación remota 9. EQUIVALENCIA DE HERRAMIENTAS 8.3. Orientaciones especı́ficas de los temas Tema 4 Semáforos. Los semáforos pueden estudiarse en el capı́tulo 4 del texto base, en concreto en las secciones 4.1 (introducción), 4.2 (definición de un semáforo), y 4.6 (inconvenientes del mecanismo de los semáforos). Los semáforos son indiscutiblemente la herramienta de PC más popular y más extendida. Todos los sistemas operativos multiprogramados están programados usando semáforos como herramienta de sincronización entre procesos. Las razones son claras: Se trata de una herramienta de bajo nivel que se puede implementar de forma casi inmediata por hardware en cualquier arquitectura que lo admita y además permite un grado de control y flexibilidad que no es posible con ninguna otra herramienta de sincronización. Además, prácticamente todos los lenguajes de programación con posiblidades de concurrencia los implementan de forma nativa, ası́ que un programa concurrente concebido con semáforos será fácilmente portable a cualquier lenguaje y cualquier plataforma, al menos en lo que se refiere a los aspectos concurrentes. Por otro lado, las desventajas de los semáforos son considerables. Se trata de una herramienta de muy bajo nivel lógico. Un programa concurrente con unos pocos semáforos puede ir desde lo difı́cil de seguir por parte de un eventual lector a lo directamente incomprensible. Es fácil cometer errores y puede resultar complejo encontrarlos. En PC, el mero hecho de que un programa se ejecute sin problemas durante un buen rato, no es óbice para que falle al instante siguiente. Los fallos pueden ser de una sutileza desconocida en la programación secuencial. Un error frecuente con respecto a los semáforos, es pensar que después de ejecutar la sentencia signal sobre un semáforo, un proceso cede la ejecución a otro. Eso no sólo no 19 8.3 Orientaciones especı́ficas de los temas es ası́, sino que raras veces ocurre. Basta releer la semántica de la operación signal para comprenderlo. Como explicación adicional de por qué no hay motivo para que ocurra esto, basta pensar en un ordenador con dos procesadores y dos procesos. Si uno de ellos ejecuta un signal, no hay ninguna razón para que el proceso ceda la CPU al otro proceso, ya que ambos pueden seguir en ejecución de forma concurrente. Es un hecho digno de mención que las secciones del libro dedicadas a la simulación de semáforos mediante las primitivas de Java son tanto obsoletas como posiblemente incorrectas. Desde hace algunas versiones de Java existe la clase semaphore de forma nativa. Por otro lado, la simulación basada en las antiguas primitivas de Java; wait, notify y notifyall tienen el inconveniente de que al llamar a notify o notifyall, el hilo que sale del estado bloqueado (del llamado waiting set) se elige de manera aleatoria, lo cual es contradictorio con la definición de los semáforos que implica la existencia de una cola, es decir, que el primero en entrar es el primero en salir. Hay que notar que esta diferencia de funcionamiento es lo bastante sutil como no afectar de forma perceptible el comportamiento de los programas, por lo que la simulación puede usarse en la práctica sin problemas, pese a que ya no hay razón para no usar la implementación nativa de los semáforos. Tema 5 Regiones crı́ticas condicionales Las regiones crı́ticas condicionales (RCC) se explican en el capı́tulo 5 del texto base, en particular en las secciones 5.1 (introducción), 5.2 (definición de RCC) y 5.5 (inconvenientes del mecanismo de RCC). Las regiones crı́ticas a secas se pueden considerar como un caso particular de región crı́tica condicional en la que la condición siempre es cierta. Las regiones crı́ticas condicionales son una herramienta de nivel conceptual superior a los semáforos. Por algún motivo, es raro que estén implementadas de forma nativa en lenguajes reales. En cualquier caso, permiten soluciones bastante elegantes para algunos problemas de PC que resultarı́an enrevesadas con semáforos. Una diferencia interesante es el uso de dos colas para bloquear los procesos. Además de la cola principal, existe una segunda cola, llamada cola de eventos, que permite que aquellos procesos que obtuvieron la exclusión mutua y después la devolvieron por no cumplir la condición, tengan preferencia a la hora de reevaluarla ante los procesos que llegan al comienzo de la región. Tema 6 Monitores El temario sobre los monitores se ajusta a lo explicado en el capı́tulo 6 del texto base, concretamente las secciones 6.1 (introducción), 6.2 (definición de monitor), 6.3 (condición de sincronización en monitores), y 6.6 (semántica de la operación resume). Los monitores representan el nivel más elevado en términos de abstracción conceptual en lo que respecta a herramientas de sincronización de memoria compartida. Esto los 20 8.3 Orientaciones especı́ficas de los temas sitúa en el extremo opuesto a los semáforos, lo que puede observarse en lo complicado que resulta simular un monitor mediante semáforos. Los monitores no han gozado históricamente de un gran apoyo en términos de implementación en lenguajes populares. El mecanismo de la exclusión mutua es automático en los procedimientos del monitor, por lo que el énfasis se sitúa lógicamente en la sincronización. A este respecto, la operación resume admite cuatro semánticas operacionales distintas. Nosotros adoptaremos la misma que el texto base, la denominada ”desbloquear y espera urgente”. Con esta semántica, el proceso que ejecuta una operación resume sale del monitor y va inmediatamente a una cola llamada cola de cortesı́a. Probablemente este comportamiento es que el despista a muchos estudiantes cuando piensan que un proceso que ejecuta un signal sobre un semáforo también se bloquea, lo que, como ya hemos mencionado, no es cierto. La situación es completamente diferente. Aunque tengamos muchos procesadores, la caracterı́stica principal del monitor es que sólo un proceso puede estar activo dentro de un procedimiento del monitor, de modo que si se quiere despertar a otro proceso que se quedó bloqueado cuando estaba dentro del monitor, hay que tomar una decisión, pero no pueden estar ejecutándose los dos al mismo tiempo. Cuando un proceso ejecuta resume dentro de un monitor pueden pasar dos cosas. La primera es que no haya ningún proceso bloqueado en la cola de cortesı́a asociada a la variable de condición. En ese caso la sentencia resume no tiene efecto. La otra es que sı́ haya procesos. Un comentario incorrecto, pero frecuente, es que el proceso que ejecuta el resume sale del monitor y entra en la cola de cortesı́a hasta que algún proceso ejecute resume, momento en el cual el proceso volverá al monitor. La cola de cortesı́a es, en efecto, una cola, por lo que si se ejecutan varios resume seguidos, un proceso que ejecutó resume después que otros puede tardar bastante en volver a entrar en el monitor; es decir, no volverá al monitor al primer resume, sino cuando le toque. La sección del libro sobre monitores en Java está totalmente desfasada, ya que desde hace unas pocas versiones los monitores están implementados en Java de forma casi nativa. Una forma sencilla de implementarlos es utilizando las clases Lock y Condition. Los equivalentes de delay y resume serı́an los métodos await y signal de Condition. Un error frecuente con los monitores es usarlos para simular semáforos, definiendo unos procedimientos exportados wait y signal, para después utilizar estos wait y signal para enmarcar una región crı́tica. Es obvio que resulta mucho más lógico aprovechar el hecho de que los procedimientos del monitor se ejecutan en exclusión mutua para hacer un procedimiento que implemente la región crı́tica. Por otra parte, es un error que demuestra poca familiaridad con el funcionamiento de los monitores. Tema 7 Buzones Los buzones corresponden a los capı́tulos 7 y 8 del texto base. En el capı́tulo 7 se siembran las bases para la comprensión de la herramienta buzones, explicando los mecanismos de paso de mensaje. Entran en el temario las secciones 7.1 (introducción), 7.2 21 8.3 Orientaciones especı́ficas de los temas (identificación en el proceso de comunicación), 7.3 (sincronización), 7.4 (canal de comunicación y mensajes) y 7.6 (espera selectiva). Los buzones propiamente dichos se explican en el capı́tulo 8, en la sección 8.1 (introducción). En este tema se entra en el campo de las herramientas basadas en paso de mensajes, en contraposición a las herramientas basadas en memoria compartida. Las herramientas de paso de mensajes son las únicas que se pueden utilizar en arquitecturas distribuidas. Aunque nada evita en principio utilizar variables compartidas, no es recomendable, ya que supone una restricción innecesaria y que priva al programa de la posibilidad de ejecutarse en sistemas distribuidos. Los buzones en sı́ se suelen declarar en el programa principal, es decir, están declarados de forma global, pero no se considera que sean variables globales, sino mecanismos de comunicación. Por esa razón, el mero hecho de usar buzones no significa utilizar memoria compartida. Conviene recordar que los buzones son herramientas bloqueantes. Si un proceso intenta leer de un buzón vacı́o, quedará bloqueado hasta que otro proceso escriba en dicho buzón. Una forma de evitar esta situación es usar adecuadamente la función EMPTY. Si el buffer es de tamaño finito, las operaciones de escritura en el buzón también provocan bloqueo cuando el buffer asociado esté lleno. Una excepción a ambas situaciones es el uso de la sentencia SELECT. Otro error común, quizás influencia de la herramienta canales, es usar un buzón distinto por cada dos procesos que quieran comunicarse entre ellos. Un mismo buzón puede ser utilizado por todos los procesos. Un último error común es olvidar que la comunicación en los buzones puede ser bidireccional. Un proceso puede utilizar un mismo buzón tanto para leer como para escribir en él. Tema 8 Invocación remota La herramienta conocida como rendez-vous, cita de Ada o invocación remota se explica en el capı́tulo 10 del texto base, en las secciones 10.1 (introducción) y 10.2 (invocación remota). La invocación remota es la herramienta de más alto nivel conceptual de todas las herramientas de sincronización basadas en paso de mensajes. Mediante la invocación remota, un proceso puede ejecutar un procedimiento de otro proceso que además puede (y suele) estar ejecutándose en otra máquina. La invocación remota es la herramienta concurrente más claramente enfocada al desarrollo de aplicaciones cliente-servidor. Una diferencia sustancial de la invocación remota respecto a cualquier otra herramienta de PC, es que el que uso de invocación remota suele llevar parejo el uso de procesos adicionales que actúan como servidor o controlador. En particular, la simulación de otras herramientas de sincronización mediante invocación remota implica la creación de nuevos procesos. A este respecto, la simulación de un semáforo mediante invocación remota en la página 332 del texto base es bastante clarificadora. 22 8.4 Bloque temático 3: Problemas de aplicación Un error común es suponer que el empleo de la invocación remota proporciona de forma ”mágica” exclusión mutua. En primer lugar, es perfectamente posible, y posiblemente conveniente, utilizar la invocación remota sin usar memoria compartida. En ese caso la cuestión ni se plantea. Si se combina memoria compartida e invocación remota puede haber problemas, especialmente si tenemos en cuenta que la invocación remota permite el paso de parámetros en ambas direcciones entre el proceso llamador y el llamado. El paso de parámetros implica copia de los mismos, por lo que si se pasan como parámetros de una invocación remota variables compartidas, los resultados pueden ser impredecibles. 8.4. Bloque temático 3: Problemas de aplicación En PC hay dos problemas-tipo cuyo conocimiento es absolutamente imprescindible: Se trata del problema de los lectores y escritores y del problema de los productores y consumidores. Multitud de los aspectos de colaboración entre los procesos de un programa concurrente responden a alguno de estos esquemas, ya sea directamente o mediante variaciones, combinaciones o generalizaciones de los mismos. Sólo por este motivo su incorporación al temario estarı́a sobradamente justificada, pero además, el estudio de su resolución mediante las diferentes herramientas del segundo bloque temático supone una excelente manera de comprender su funcionamiento en la práctica. Por otra parte, es lógico suponer que también hay problemas que no responden en absoluto a dichos esquemas algorı́tmicos. El otro problema resuelto en el texto base usando todas las herramientas de sincronización es el problema de la comida de los filósofos. Se trata de un problema clásico propuesto por Andrew Tanembaum para ilustrar muchos de los problemas que se producen al intentar solucionar un problema de PC de apariencia inocente, pero que no tiene una solución plenamente satisfactoria. 8.4.1. Objetivos Reflexionar sobre el uso de cada herramienta y despejar dudas sobre su funcionamiento. Al completar este bloque temático, se espera que el estudiante sea capaz de identificar un problema-tipo si se enfrenta a uno en alguna de las pruebas de la asignatura. 8.5. Orientaciones sobre los contenidos Este bloque temático consta de un único tema: 23 9 Actividades Tema 10 Problemas de aplicación Los problemas de aplicación pueden estudiarse en las siguientes secciones del texto base: 4.3 (resolución de problemas usando semáforos), 5.3 (resolución de problemas usando RCC), 6.4 (resolución de problemas usando monitores), la 8.2 (resolución de problemas empleando paso de mensajes ası́ncrono) y 10.3 (resolución de problemas mediante invocación remota en PASCAL-FC). En ocasiones es necesario que varios procesos puedan leer un recurso al mismo tiempo. Si el recurso no tiene siempre el mismo valor, cosa que tendrı́a poco sentido, también habrá procesos que intenten modificarlo. La primera restricción descarta el acceso en exclusión mutua al recurso. La segunda descarta el acceder al recurso compartido sin ningún tipo de sincronización. El esquema algorı́tmico de los lectores y escritores es un ingenioso mecanismo que permite satisfacer ambas restricciones al mismo tiempo. El texto base trata la cuestión desde un punto de vista bastante teórico. Como la división en capı́tulos se realiza en función de las diferentes herramientas, los problemas resueltos y propuestos al final de cada capı́tulo suelen restringirse a proponer problemas de ı́ndole general de cada herramienta concreta. En el curso virtual puede encontrarse una cierta cantidad de exámenes resueltos que enfocan los problemas-tipo. En la implementación de lectores y escritores con preferencia para la escritura resuelto mediante semáforos, en la página 101 del texto base, hay una errata: Donde pone ”ne := ne -1;”, deberı́a poner ”escribiendo := false”. El problema-tipo de los productores y consumidores suele provocar menos problemas de comprensión que el de los lectores y escritores, por lo que se recomienda al estudiante que intensifique su estudio de este segundo. El problema de la cena de los filósofos no es un problema-tipo, sino más bien el tipo de problema que no se ajusta a ningún tipo, por lo que identificar cualquier problema (excepto el de los filósofos y sus variaciones, claro está) como un caso del problema-tipo de los filósofos es un hecho bizarro que se recomienda evitar. 9. Actividades En el texto base y la bibliografı́a complementaria pueden encontrarse numerosos ejercicios propuestos y resueltos. Del mismo modo, en el curso virtual pueden encontrarse prácticas y exámenes de otros años, algunos de ellos resueltos. 24