Compartición de Datos, Bloqueo GIL e Hilos en Python

Compartición de Datos

from multiprocessing import Process, Lock, Manager

def modificar_contador(contador, candado):
    with candado:
        contador['valor'] -= 1

if __name__ == '__main__':
    with Manager() as administrador:
        candado = Lock()
        contador_compartido = administrador.dict({'valor': 10})
        procesos = []
        
        for i in range(10):
            p = Process(target=modificar_contador, args=(contador_compartido, candado))
            procesos.append(p)
            p.start()
        
        for p in procesos:
            p.join()
        
        print(contador_compartido)

Fundamentos de Hilos y Bloqueo GIL

  • Procesos: Aislamiento de datos, unidad mínima de asignación de recursos, utilizan múltiples núcleos, gestionados por el SO, alta sobrecarga en creación/cierre
    • Número típico: no superar el doble de núcleos CPU
    • Uso básico: start() y join()
    • Problemas de seguridad: Lock
    • Comunicación entre procesos (IPC):
      • Colas (seguras), tuberías (inseguras)
      • Herramientas externas
    • Compartición de datos mediante Manager
    • Modelo productor-consumidor
  • Hilos: Unidad mínima programable por el SO
    • Comparten datos entre sí
    • Baja sobrecarga en creación/cierre
    • Limitaciones en CPython por GIL

GIL en CPython

  • No permite uso de múltiples núcleos para hilos
  • Mecanismo de recolección de basura:
    • Conteo de refeerncias + recolección generacional
  • Bloqueo de Intérprete Global (GIL) asegura precisión en conteo de referencias
  • Restringe ejecución a un único hilo por proceso en CPU
  • Optimiza tiempo de operaciones I/O, no cómputo CPU

Alternativas de Intérprete

  • PyPy: Mantiene limitaciones de GIL
  • JPython: Permite uso de múltiples núcleos

Módulo Threading

  • enumerate(): Lista de objetos de hilos activos
  • active_count(): Cantidad de hilos activos
  • current_thread(): Obtiene hilo actual (ident devuelve ID)
import time
from threading import Thread, current_thread
import os

def tarea(indice):
    print(f'inicio{indice}', current_thread().ident)
    time.sleep(1)
    print(f'fin{indice}')

if __name__ == '__main__':
    hilos = []
    for i in range(10):
        h = Thread(target=tarea, args=(i,))
        h.start()
        print(h.ident, os.getpid())
        hilos.append(h)
    
    for h in hilos:
        h.join()
    
    print('Ejecución completada')

Hilos con Orientación a Objetos

from threading import Thread

class HiloPersonalizado(Thread):
    def __init__(self, param1, param2):
        self.param1 = param1
        self.param2 = param2
        super().__init__()
    
    def run(self):
        print(self.ident, self.param1, self.param2)

h = HiloPersonalizado(1, 2)
h.start()
print(h.ident)

Compartición de Datos entre Hilos

from threading import Thread

valor_global = 100

def reducir():
    global valor_global
    valor_global -= 1

hilos = []
for _ in range(100):
    h = Thread(target=reducir)
    h.start()
    hilos.append(h)

for h in hilos:
    h.join()

print(valor_global)  # Resultado: 0

Etiquetas: Python GIL multiprocessing threading concurrencia

Publicado el 6-25 19:05