# 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 ```