Apuntes_Python/02_conceptos/15_threading_vs_multiprocessing/README.md

164 lines
5.2 KiB
Markdown
Raw Normal View History

2022-12-24 22:41:20 -03:00
# Threading y Multiprocessing
Con Threading y Multiprocessing, se puede ejecutar código en paralelo y
mejorar los tiempos de ejecución.
Ejemplos:
- [Multiprocessing](https://gitea.kickto.net/devfzn/Apuntes_Python/src/branch/master/02_conceptos/15_threading_vs_multiprocessing#multiprocessing)
- [Threading](https://gitea.kickto.net/devfzn/Apuntes_Python/src/branch/master/02_conceptos/15_threading_vs_multiprocessing#threading)
## Process / Proceso:
Una instancia de un programa (ej, el interprete de Python)
+ Aprovecha multiples CPUs y Nucleos.
+ Espacios de memoria separados -> memoria no compartida entre procesos
+ Genial para procesamiento dependiente de la CPU (CPU-bound processing)
+ Nuevos procesos son independientes de otros procesos
+ Los procesos son interruptable/killable
+ Un GIL para cada proceso -> evita las limitaciones [GIL](https://python.land/python-concurrency/the-python-gil)
- Son pesados y utilizan mas memoria
- Iniciar un proceso es mas lento que iniciar un thread
- IPC (inter-process communication) es mas complicada
[Pydoc multiprocessing](https://docs.python.org/3/library/multiprocessing.html#module-multiprocessing)
----
## Threads / Hilo:
Una entidad en/dentro de un proceso que puede ser programado (también conocido como 'proceso liviano')
Un proceso puede generar multiples hilos.
+ Todos los hilos dentro de un proceso comparten la misma memoria
+ Son mas livianos
+ Uniciar un hilo es mas rápido que iniciar un proceso
+ Buenos para tareas 'I/O-bound' (tareas de entrada y salida, ej. hdd, network)
- Los hilos son limitados por GIL: Solo un hilo a la vez
- Sin efecto para tareas 'CPU-bond' (cpudependientes)
- No son interruptable/killable
- Se debe tener cuidado con condiciones de carrera (cuando 2 o mas hilos necesitan
modificar la misma variable al mismo tiempo)
[Pydoc threading](https://docs.python.org/3/library/threading.html#module-threading)
----
## GIL: Global Interpreter Lock
- Es un bloqueo que solo permite la ejecución de un hilo a la vez en python
- Es necesaria en CPython porque la administración de memoria no es 'thread-safe'
[Pydoc GIL](https://docs.python.org/3/c-api/init.html?highlight=gil#thread-state-and-the-global-interpreter-lock)
**Para evitar GIL:**
- Usar multiprocessing
- Utilizar una implementación de Python diferente, 'free-threaded' (Jython, IronPython)
- Usar Python como un envoltorio para librerias de terceros (C/C++) -> numpy, scipy
----
# Multiprocessing
```python
from multiprocessing import Process
import os, time
def nros_cuadrados():
"""
función dummy para ser ejecutada por Process 'target'
"""
for i in range(100):
i * i
time.sleep(0.1)
# lista que guarda todos los procesos
procesos = []
# un buen número de procesos es la cantidad de CPUs de la maquina
num_procesos = os.cpu_count()
# creación de los procesos
for i in range(num_procesos):
# Process toma 2 argumentos
# función target
p = Process(target=nros_cuadrados) # , args=(tupla_args))
procesos.append(p)
# Iniciar procesos
for p in procesos:
p.start()
# join (bloquear el hilo principal hasta terminar ejecución de procesos)
for p in procesos:
p.join()
print("FIN main")
```
*Ejemplo de la salida de Htop*
```
20551 root 20 0 8732 S 0.0 0.1 0:00.00 1 python3 multi.py
20552 root 20 0 8732 S 0.0 0.1 0:00.00 1 python3 multi.py
20553 root 20 0 8732 S 0.0 0.1 0:00.00 1 python3 multi.py
20554 root 20 0 8732 S 0.0 0.1 0:00.00 1 python3 multi.py
20555 root 20 0 8732 S 0.0 0.1 0:00.00 1 python3 multi.py
20556 root 20 0 8732 S 0.0 0.1 0:00.00 1 python3 multi.py
20557 root 20 0 8736 S 0.0 0.1 0:00.00 1 python3 multi.py
20558 root 20 0 8736 S 0.0 0.1 0:00.00 1 python3 multi.py
```
----
# Threading
```python
from threading import Thread
import os, time
def nros_cuadrados():
"""
función dummy para ser ejecutada por Thread 'target'
"""
for i in range(100):
i * i
time.sleep(0.1)
# lista que guarda todos los threads
hilos = []
num_hilos = 10
# creación de los hilos
for i in range(num_hilos):
# Thread toma 2 argumentos
# función target
h = Thread(target=nros_cuadrados) # , args=(tupla_args))
hilos.append(h)
# Iniciar hilos
for h in hilos:
h.start()
# join (bloquear el hilo principal hasta terminar ejecución de procesos)
for h in hilos:
h.join()
print("FIN main")
```
*Ejemplo de la salida de Htop*
```
23903 root 20 0 8732 S 0.0 0.1 0:00.00 11 python3 b_multit.py
23904 root 20 0 8732 S 0.0 0.1 0:00.00 11 python3 b_multit.py
23905 root 20 0 8732 S 0.0 0.1 0:00.00 11 python3 b_multit.py
23906 root 20 0 8732 S 0.0 0.1 0:00.00 11 python3 b_multit.py
23907 root 20 0 8732 S 0.0 0.1 0:00.00 11 python3 b_multit.py
23908 root 20 0 8732 S 0.0 0.1 0:00.00 11 python3 b_multit.py
23909 root 20 0 8732 S 0.0 0.1 0:00.00 11 python3 b_multit.py
23910 root 20 0 8732 S 0.0 0.1 0:00.00 11 python3 b_multit.py
23911 root 20 0 8732 S 0.0 0.1 0:00.00 11 python3 b_multit.py
23912 root 20 0 8732 S 0.0 0.1 0:00.00 11 python3 b_multit.py
```