Apuntes_Python/02_conceptos/15_threading_vs_multiprocessing/README.md

5.2 KiB

Threading y Multiprocessing

Con Threading y Multiprocessing, se puede ejecutar código en paralelo y
mejorar los tiempos de ejecución.

Ejemplos:

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
  • 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


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


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

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

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

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