Un poco de concurrencia

Historia

Desarrollar aplicaciones que impliquen manejar concurrencia es difícil.

Empecemos con un poco de historia para entrar en contexto; en un principio las computadoras no tenían sistemas operativos, solo recibían el código de algun programa, lo procesaban y listo. Todo esto ocurría en un solo proceso aislado. Dicho proceso tenía acceso a todos los recursos de la computadora. Cuando la ejecución del programa terminaba, la computadora estaba lista para procesar otro programa. Ejecutar un programa y tener que esperar cada vez a que terminara para ejecutar el siguiente programa era algo muy ineficiente.

Con la evolución de los sistemas operativos se logró que más de un programa pudiera correr al mismo tiempo mediante procesos. Dichos procesos se ejecutan aisladamente unos de otros y el sistema operativo es el que asigna recursos como memoria y acceso a disco a dichos procesos. Si un proceso requiere comunicarse con otro proceso, es el sistema operativo el que brinda medios comunicación como sockets, shared memory, semáforos y archivos para hacerlo.

Cada proceso era una representación de la arquitectura de von Neumann. Tenían asignado un espacio de memoria para almacenar instrucciones y datos. Las instrucciones eran ejecutadas secuencialmente. Por cada instrucción se definía la siguiente instrucción a ejecutar después.

Threads

Con el paso del tiempo se tuvo la necesidad de optimizar la manera en cómo los procesos se ejecutaban y aparecieron los Threads. Con la llegada de los threads se logró tener múltiples flujos de control coexistiendo dentro de un proceso. Los threads comparten recursos como memoria y acceso a disco pero cada thread tiene su propio stack y variables locales. Otra característica de los threads es que pueden ejecutarse simultáneamente en los distintos CPU’s de una computadora (paralelismo).

Los threads también simplificaron el diseño de las aplicaciones, pues un thread se consideró como la entidad mínima en un programa para ejecutar una tarea. Un programa se puede diseñar a partir de las tareas que resuelve y modelar dichas tareas a través de threads fue relativamente simple.

Otro característica que se simplificó con los threads fue el manejo de eventos asíncronos. Una aplicación cliente-servidor que procesa requests lo puede hacer asíncronamente a través de threads, es decir, múltiples requests que lleguen al mismo tiempo pueden atenderse simultáneamente al ser delegadas a distintos threads. En una aplicación single-thread cada request tendría que ser encolada y esperar su turno para ser procesada, si un request toma mucho tiempo al procesar I/O los requests subsecuentes tendrán que esperar…

Como se mencionó anteriormente, el sistema operativo es el encargado de la ejecución de procesos y threads. Independientemente del lenguaje de programación que utilices y sus diferentes mecanismos de abstracción de paralelismo y concurrencia, al final, en el sistema operativo procesos y threads son los responsables de ejecutar las tareas de tu programa. Los lenguajes de programación tienen abstracciones para manejar a un nivel un poco más alto procesos y threads.