Codea Blog  

Blog Details

Programacion Concurrente y Paralela en PYTHON

1. Procesos e Hilos

Un proceso es una instancia del programa de computadora, donde cada proceso tiene un espacio individual en la memoria, donde almacena las instrucciones a ejecutar y otros datos que necesite acceder o almacenar para la ejecución del código.

 

Un hilo(también conocido como subproceso) es un flujo de ejecución independiente. Puede verse como un componente de un proceso, que puede ejecutarse en paralelo. Puede haber múltiples hilos en un mismo proceso, donde estos comparten el mismo espacio de memoria, compartiendo así el código a ejecutar y variables en el programa. Un proceso requiere mas tiempo y es mas lento en comparación con un subproceso, podemos decir que un conjunto de hilos son parte de un mismo proceso.

 

2. Concurrencia

Es la capacidad de un sistema para procesar mas de un hilos de ejecución(procesos) al mismo tiempo. Los procesos se dicen que son concurrentes por que coexisten al mismo tiempo

 

La concurrencia puede darse en un sistema mono-procesador , es decir un solo procesador(core). Hay concurrencia si los procesos “conviven” en el mismo instante de tiempo , no es necesario que en el mismo instante de tiempo se estén ejecutando a la vez.   

                          

 

 

 

 

En caso de un procesador , para la ejecución de estos tres procesos se da turnándose en instantes de tiempo muy cortos , donde en cada tiempo se ejecuta una parte de uno de los procesos , y cuando este tiempo acaba le precede la otra parte de otro proceso en un mismo o diferente instante de tiempo , habrá partes de procesos mas importantes que otros y a estos se les dará un mayor tamaño tiempo .Podemos decir que no hay procesos que se ejecuten a la vez pero estos si “conviven” entre si.

 

En caso de mas de un procesador , se da de igual manera , repartiéndose el tiempo entre partes de los procesos , donde solo se puede ejecutar una de las partes de un procesos a la vez en el procesador. La diferencia es que estas partes que conforman el proceso pueden ser procesadas en diferentes procesadores. Puede darse el caso en que el numero de procesos sea igual al números de procesadores , en este caso puede darse una ejecución “simultanea” donde todos estos procesos conviven en el mismo instante de tiempo.

 

3. Paralelismo

Es la capacidad de un sistema para ejecutar mas de un hilo de ejecución (proceso) al mismo tiempo . Los procesos paralelos se da cuando estos procesos se dan en diferentes procesadores al mismo tiempo.

 

En resumen el paralelismo implica concurrencia , si varios procesos se ejecutan al mismo tiempo(son paralelos), eso implica que conviven( existen) a la vez. Por tanto son concurrentes.

 

La concurrencia no implica paralelismo , si varios procesos conviven a la vez (son concurrentes), esto no implica que deban ejecutarse a la vez. Por lo tanto no necesariamente son paralelos

 

4. Context switches

Un cambio de contexto es almacenar el estado de un proceso o subproceso, de modo que pueda restaurarse y reanudar la ejecución en un punto posterior, y luego restaurar un estado diferente, previamente guardado. Esto permite que múltiples procesos compartan una sola unidad central de procesamiento y es una característica esencial de un sistema operativo multiprogramación o multitarea . Es decir al ejecutar varios programas en un mismo core , cada ejecución de un proceso/hilo se da en pequeñas partes , primero se ejecuta una parte de las instrucciones de un programa luego esta data generada lo deja el cpu y procede a realizar lo necesario para ejecutar un parte de las instrucciones del siguiente programa , este cambio de contexto genera un costo de procesamiento de cpu en tiempo. Entonces se realizan pausas entre ejecuciones de instrucciones de programas diferentes , lo cual puede provocar un error al detener la ejecución de algo importante como al trabajar con datos que requieren ser constantes durante la ejecución de un proceso , si se pausa su ejecución y se da paso a otro proceso podría ingresar a estos datos y alterarlos y por lo tanto causar un error en el proceso anterior.

 

La soluciones posibles de estos problemas pueden ser

 

- No compartir la variable de estado entre subprocesos

- Hacer que la variable de estado sea inmutable

- Utilizar la sincronización cada vez que acceda a la variable de estado

 

Para esto se puede usar los mecanismos como Race Conditions, Locks, Semaphores y Deadlocks

 

5. Esperas de I/O

Los hilos que se ejecutan del programa , muchas veces van a hacer operaciones donde pasaran un buen tiempo sin necesidad de ejecutar instrucciones , es decir tiempo donde un programa espera el resultado de otro recurso . Este tiempo donde el cpu no se usa podría ser aprovechado por otro hilo , hasta que el hilo anterior haya ya recibido su recurso que estaba esperando .

Como los procesos tiene su propia ram y todos los hilos son parte de un mismo proceso , es fácil compartir datos entre hilo de un mismo procesos , pero no es fácil compartir datos entre procesos distintos , entonces necesitaran de algún mecanismo para esto.

 

5.1. Programacion sin esperas de I/O

Aquí el paralelismo ayuda a reducir tiempos . La Concurrencia sin paralelismo no reduce el tiempo en programas de input – outpot. Como se da el caso en computadoras de un núcleo

 

5.2. Programas con esperas I/O

La concurrencia sin paralelismo igual ayuda a acelerar , por mas q la concurrencia no tenga paralelismo . Como podemos ver la siguiente imagen hay dos hilos de diferentes colores , donde se ve que hay tiempos de espera(cuadros vacíos) , los cuales se pueden aprovechar con la ejecución de una parte del otro hilo , así reduciendo el tiempo de ejecución de un proceso sin necesidad del paralelismo.

 

Se puede dar una combinación de ambos , lo que resulta ser mas optimo en tiempo , es decir paralelismo con varios Cores, concurrencia en cada Core

 

6. Paralelismo y concurrencia en Python

La mayoría de lenguajes de programación soportan programación concurrente y en paralelo , pero en Python no es así ya que Python esta diseñado para que solo un thread (hilo) se ejecute a la vez. El encargado de que solo un thread tome el control del interprete es Global Interepter Lock (GIL)  

 

Para poder ejecutar las tareas en paralelismo debemos optar por el multiprocesamiento sobre el multihreading, donde cada proceso tendrá su propio interprete y podrá ejecutarse de manera independiente . Por lo tanto, no hay necesidad de un GIL mientras se realiza el multiprocesamiento, ya que todos los procesos son independientes. Por lo tanto, el procesamiento múltiple nos proporciona el paralelismo real en Python.

 

 

6.1. Threading

Para hacer multi threading Python tiene el modulo threading que permite lanzar hilos y controlarlos (preguntar su estado, frenar un hilo , lanzar uno nuevo ,etc). Aquí si tenemos concurrencia

 

¿El multi threading permite el paralelismo real? , depende En CPython (el oficial) no soporta el paralelismo real a causa de GIL, aunque tengamos varios cores.

 

En otros Python como Jython y IronPython si hay paraelismo real ya que estos no poseen a GIL.

 

En Python los hilos comparten memorias , lo que facilita usar los datos de un hilo en otro . Por ejemplo un programa Python que use el modulo threading , que tenga varios hilos, toda la memoria es compartida pudiendo así tener una lista donde un hilo podría meter elementos a esta lista y otro hilo consultando si hay y hasta sacando estos elementos , y así sin tener que definir algo en especfico para este funcionamiento ya que solo hay un hilo ejecutándose al mismo tiempo. En la pausa de un hilo y el incio de otro , se puede dar errores que se comprenden del context switches.

 

En caso de muchas tareas (hilos) o esperas de I/O muy largos , el context switch hace que no sea eficiente.

 

En Python El modo más sencillo para usar un hilo es instanciar un objeto de la clase Thread con una función objetivo y hacer una llamada a su método start().

 

Con estos hilos podremos hacer que se ejecuten las instrucciones de una función, como en el siguiente ejemplo vemos que para cada llamada de función se le atribuye un hilo , creando así 3 hilos para 3 llamadas a la función.

 

 

El método start() incia la actividad del hilo . El método join() , espera a que el hilo termine para finalizar la ejecución del programa.

 

De esta manera podemos hacer que se ejecuten las instrucciones de funciones diferentes al mismo tiempo , de manera concurrente, ya que cada hilo llama a su correspondiente función al que fue referenciado.

 

6.2. Multiprocessing

El modulo multiprocessing nos permite lanzar procesos y controlarlos . El multiprocessing ofrece paralelismo real si se cuenta con mas de un core . Cada proceso tiene su memoria , donde cada procesos tiene todo lo necesario para correr , es decir hay mucha memoria repetida(librerías, GIL ,etc). 

 

Sobre compartir datos , entre procesos resulta mas complicado que el threading . Python ofrece herramientas ,como Manager(), que ayudan para tener datos compartidos entre procesos y su control.

 

Si se tiene muchas tareas (procesos) o esperas de I/O muy largos se empieza a ser menos eficiente por el context switch, es decir hay una sobrecarga de tiempos de cambio entre procesos.

 

Para usar multiprocesos usamos la librería “multiprocessing” e importamos “Process”. En la siguiente imagen podemos ver un ejemplo básico sobre instanciar procesos asignando la función a ejecutar.

 

 

6.3. Concurrent Futures

Es un módulo de la biblioteca estándar de Python que proporciona una interfaz de alto nivel para la programación “concurrente”. Permite ejecutar tareas de forma paralela o en segundo plano, ya sea utilizando multiprocessing o threading .

 

Su objetivo principal es simplificar la programación concurrente, permitiendo que las tareas se ejecuten en paralelo y se gestionen de manera eficiente.

 

La clase principal en el módulo concurrent.futures es Executor, que define una interfaz común para programar y ejecutar tareas en paralelo. Hay dos implementaciones principales de Executor:

 

ThreadPoolExecutor: Esta implementación utiliza hilos para ejecutar las tareas en paralelo dentro de un solo proceso. Puedes crear un objeto ThreadPoolExecutor para ejecutar tareas de forma concurrente utilizando hilos.

ProcessPoolExecutor: Esta implementación utiliza procesos para ejecutar las tareas en paralelo. Puedes crear un objeto ProcessPoolExecutor para ejecutar tareas de forma concurrente utilizando procesos.

 

En resumen, Multithreading es concurrencia. El multiprocessing es paralelismo

 

El multiprocesamiento se basa en la ejecución de múltiples procesos independientes con su propia memoria, mientras que el multihilo se basa en la ejecución de múltiples hilos dentro de un solo proceso compartiendo la misma memoria. La elección entre multiprocesamiento y multihilo depende de los requisitos y características específicos de la aplicación, como el rendimiento deseado, la naturaleza de las tareas a realizar y la necesidad de compartir o separar la memoria y los recursos del sistema.

 

Recordemos que la implementación estándar de Python se conoce como Cpython que es el que viene implementado con GIL el cual nos impide ejecutar instrucción en mas de un hilo. Hay mecanismos como librerías que nos ayudan a evitar estas limitaciones.

 

Las librerías threading, concurrent futures y multiprocessing son las que nos ofrece Python para poder realizar la ejecución simultanea de tareas y el procesamiento paralelo. Estas librería ofrecen mas metodos para poder tener un mejor performance del programa que realicemos, metodos que se pueden encontrar en la documentación de Python.

 

La programación concurrente y paralela se utilizan para lograr un mejor rendimiento y aprovechar al máximo los recursos de hardware disponibles , logrando un tiempo de ejecucion mas corto.

 


7. REFERENCIAS

1. https://docs.python.org/es/3/library/concurrency.html

2. https://docs.python.org/3/library/concurrent.futures.html

3. PyAr (2021), Concurrencia y paralelismo en Python: Multithreading vs Multiprocessing vs Async https://www.youtube.com/watch?v=u77Az26bFPA&t=2182s&ab_channel=PyAr-PythonArgentina

4. Universidad Politecnica de Valencia(2020) Concurrencia vs. Paralelismo https://www.youtube.com/watch?v=kMr3mF71Kp4&list=LL&index=2&ab_channel=UniversitatPolit%C3%A8cnicadeVal%C3%A8ncia-UPV

5. Anonimo(sin fecha) Codigofacilito, ¿Qué es GIL en Python?https://codigofacilito.com/articulos/gil-python

6. Harshvardhan Anand(2020) medium.com, Multitasking In Python https://medium.com/harshvardhan-ai/multithreading-multiprocessing-threading-in-python-harshvardhan-anand-6a478b458c31 

7. Mario Rossainz López (sin fecha) INTRODUCCIÓN A LA PROGRAMACIÓN CONCURRENTE Y PARALELA, Paralelismo y concurrencia pag 2-5 http://rossainz.cs.buap.mx/PCyP_Maestria/1_Apuntes/1_Introduccion_PCyP.pdf

 

Comentarios

Registrate o Inicia Sesión para comentar y obtener Cursos de pago gratis

function loadurl(){ var val1 = document.getElementById("valor3").value; console.log(val1); if(val1){ window.location = "/comunidad/blog/filtrar/"+val1+"/"} }