Libro Python Aplicado de Eugenia Bahit. GNU/Linux, ciencia de datos, y desarrollo web

Banner de Python Aplicado

Paralelismo y concurrencia


Cita con formato IEEE:
E. Bahit, "Paralelismo y concurrencia", in Python Aplicado, 4th ed., EBRC Publisher, 2022, pp. 145-164.

Cita con formato APA 7:
Bahit, E. (2022). Paralelismo y concurrencia. In Python Aplicado (4th ed., pp. 145-164). EBRC Publisher.

Cita en línea:
(Bahit, 2022)

Paralelismo y concurrencia son dos conceptos abstractos que hacen referencia —en la práctica aplicada—, a la multiprogramación (o programación en paralelo) y a la administración de la ejecución de múltiples procesos.

Paralelismo: conjunto de técnicas empleadas para procesar información de forma simultánea, con el objetivo de reducir la cantidad de tiempo que consume la ejecución de un programa.

Concurrencia: forma de gestionar y coordinar la ejecución de múltiples procesos.

Proceso: programa en ejecución.

Entender el paralelismo

Para entender el paralelismo es necesario comprender cómo trabaja un procesador. Cuando los programas se ejecutan se crean procesos. Si hay más de un programa en ejecución, hay por lo menos, un proceso por cada uno de ellos. Cada procesador, en un mismo instante de tiempo, puede ejecutar uno —y solo un— proceso, por lo que cada uno de ellos se van ejecutando secuencial y alternadamente, y es el procesador quien decide qué procesos y en qué momento, darles prioridad, siendo entonces, quien administra los recursos.

Cuando un programa se ejecuta y un proceso es creado, este puede permanecer en uno de tres estados posibles:

Multiprogramación: cuando varios procesos se ejecutan simultáneamente en una misma CPU, y en realidad la CPU está alternando rápidamente entre un proceso y otro, a no ser que realmente, el ordenador cuente con más de una CPU.

En el modelo de procesamiento más simple, un programa ejecutará una única tarea en simultáneo, por lo que solo requerirá de un único proceso. Sin embargo, es habitual que en un programa se requieran múltiples tareas siendo ejecutadas simultáneamente. Crear nuevos procesos para dichas tareas no es viable, pues todos los procesos compartirían el mismo espacio de memoria, ya que cada programa posee el suyo. Para resolverlo, se generan unos procesos más pequeños dentro del proceso principal, llamados hilos.

Al crear hilos en paralelo, un mismo programa puede ejecutar varias tareas de forma casi simultánea, por lo que se reduce la cantidad de tiempo que el programa insume en completar las mismas. De esta forma, en sistemas con múltiples CPU donde la ejecución en paralelo es posible, la velocidad de ejecución de los programas se incrementa considerablemente.

La capacidad de soportar múltiples hilos en un mismo proceso se conoce como multithreading.

Problemas con el paralelismo

En la programación paralela, cuando dos procesos trabajan de forma simultánea compartiendo un mismo conjunto de datos, intentan escribir sobre , el resultado depende de cuál de los dos procesos más rápido y escribir primero. Esta situación se conoce como condición de carrera.

Para evitar las condiciones de carrera, se deben implementar mecanismos que eviten que dos o más procesos, lean y/o escriban simultáneamente sobre el mismo conjunto de datos. Esto se conoce como exclusión mutua.

Programación paralela en Python

La biblioteca nativa de Python, a través de sus módulos threading y multiprocessing, provee soporte para multiprogramación basada en hilos y en procesos respectivamente.

Paralelismo basado en hilos

El script (o programa) que se haya creado, será el proceso en ejecución que se dividirá en hilos. Se crearán tantos hilos como se desee, y cada hilo deberá ser inicializado luego de su creación.

Si se hace un repaso de los estados de un proceso, pueden trasladarse a los hilos: creado (listo), en ejecución y bloqueado. Para aplicar cada cambio de estado en Python, se recurre al módulo threading, mediante el cual, se crean nuevos hilos con la clase Thread, se ejecutan con la llamada al método start, y se bloquean con el método join.

A continuación, se crean cinco hilos que ejecutarán una función (la cual debió haber sido definida previamente) llamada MiFunción:

from threading import Thread

for i in range(0, 5):
hilo = Thread(target=MiFuncion)
hilo.start()
hilo.join()