UC3M

Grado en Ing. Telemática/Sist. Audiovisuales/Sist. de Comunicaciones

Arquitectura de Sistemas

Septiembre 2017 - Enero 2018

Capítulo 11. Los hilos

11.1. Programación concurrente

Formalmente, la computación concurrente se define como una forma de computación donde las ejecuciones se producen en tiempos de ejecución solapados -esto es "concurrentemente"- en vez de ejecutarse secuencialmente (uno completando su ejecución antes de que el siguiente empiece con la suya propia).

  • Un sistema concurrente se caracteriza por poder progresar sin tener que esperar a que terminen todas sus computaciones previas. También se caracteriza por que una o más computaciones pueden progresar al mismo tiempo.

  • Como paradigma de programación, la programación concurrente es una forma de programación modular, denominada factorizing donde un bloque de computación se ejecuta con el resto.

Una definición más informal sería la de una forma de computación donde las diferentes entidades se solapan para conseguir un mejor rendimiento. Un ejemplo muy cercano es nuestro entorno de trabajo dotado de un ordenador personal donde tenemos un editor, un compilador, un navegador y otras aplicaciones que se ejecutan concurrentemente. En este tipo de infraestructura no esperamos a que una acabe una tarea para comenzar con la siguiente.

Muchos otros sistemas concurrentes se refieren a ejemplos como los aeropuertos o aplicaciones ferroviarias donde el uso de este tipo técnicas reducen los tiempos de ejecución notablemente y hacen un mejor uso de una infraestructura, en principio cara.

Veamos algunas de las potenciales ventajas provistas por la computación concurrente:

  • Mejoras en el rendimiento de las aplicaciones. Al ejecutarse concurrentemente, las aplicaciones tienen tendencia a terminar antes pues se puede hacer uso más eficiente de los recursos. Esto es bastante interesante para las computadoras actuales donde imperan los sistemas con varios núcleos (o cores, en inglés).

  • Mejoras en los tiempos de respuesta de las operaciones de entrada/salida. Muchas applicaciones, que se bloquean en la entrada y la salida para completarse, se bloquean menos cuando se ejecutan concurrentemente.

  • Permiten que el tiempo de CPU que no usen las aplicaciones quede disponible para otras tareas.

  • Adecuado para resolver "ciertos" problemas de forma concurrente. Hay ciertos problemas que se pueden resolver concurrentemente más sencillamente. Un ejemplo clásico es buscar la salida a un laberinto (donde se puede lanzar una hebra para recorrer cada uno de los caminos). Otros algoritmos como son la búsqueda de datos en una estructura de datos se pueden hacer más eficientemente con soporte concurrente.

Una de las librerías que permite trabajar concurrente son las hebras POSIX, disponible en Linux. Entre todas las posibilidades y funcionalidades con las que está dotada (hay más de 100 funciones en pthread) cabe destacar aquellas encargadas de:

  • Gestión de hilos: que permiten su creación y unión. Permite crear unidades de ejecución concurrente y gestionar su ciclo de vida, gestionando un resultado al final de la ejecución de cada hebra.

  • Cerrojos (denominados en inglés como mutex). Sincronizan el acceso a datos compartidos entre hilos. Típicamente evitan problemas de lecturas y escrituras corruptas (conocidadas como "condiciones de carrera").

El resto de este documento explica este tipo de interfaz POSIX con ejemplos guiados.