164 lines
5.2 KiB
Markdown
164 lines
5.2 KiB
Markdown
|
# 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
|
||
|
```
|