GIL de Python: hilos, CPU y rendimiento

Actualizado el: 20/08/2026
Tempo de leitura: 5 minutos
Entendendo como o GIL afeta performance em Python

El GIL de Python, o Global Interpreter Lock, es un mecanismo presente en la implementación tradicional de CPython que permite que un solo hilo ejecute bytecode de Python a la vez dentro de un proceso. Esta característica simplifica parte de la gestión interna de memoria, pero también limita el paralelismo de hilos en tareas intensivas de CPU.

El GIL no significa que Python no pueda ejecutar tareas concurrentes ni aprovechar varios núcleos. Significa que debes elegir la herramienta adecuada según el tipo de trabajo: threading para espera de entrada y salida, multiprocessing para cálculo paralelo, asyncio para muchas operaciones cooperativas y bibliotecas nativas para procesamiento vectorizado.

Qué protege el GIL

CPython utiliza conteo de referencias para administrar muchos objetos. El GIL evita que varios hilos modifiquen ciertas estructuras internas al mismo tiempo sin una coordinación mucho más compleja.

La documentación oficial del módulo threading de Python explica que, debido al GIL, solo un hilo puede ejecutar bytecode de Python a la vez en una compilación estándar de CPython.

El GIL no bloquea todo el programa

Cuando un hilo espera una operación de red, disco o sistema, CPython puede liberar el GIL para que otro hilo continúe. Por eso threading puede mejorar mucho aplicaciones con tareas de entrada y salida.

import threading
import time


def wait_for_service(name):
    print(f"Iniciando {name}")
    time.sleep(2)
    print(f"Terminando {name}")


threads = [
    threading.Thread(
        target=wait_for_service,
        args=(f"task-{index}",),
    )
    for index in range(4)
]

for thread in threads:
    thread.start()

for thread in threads:
    thread.join()

sleep() simula una espera. Los cuatro hilos pueden avanzar de manera solapada porque no están realizando cálculo de Python continuamente.

Tareas de CPU y tareas de I/O

  • CPU-bound: compresión, cálculo numérico puro, transformaciones intensivas, búsqueda exhaustiva o procesamiento de imágenes escrito en Python.
  • I/O-bound: solicitudes HTTP, lectura de archivos, consultas a bases de datos, espera de sockets o servicios externos.

La elección de concurrencia depende principalmente de esta diferencia.

Por qué varios hilos no aceleran siempre la CPU

def count_down(value):
    while value > 0:
        value -= 1

Si dos hilos ejecutan esta función, deben alternarse para obtener el GIL. El cambio de contexto añade trabajo y el tiempo total puede ser igual o incluso peor que una ejecución secuencial.

La guía sobre scripts lentos en Python explica por qué conviene medir antes de cambiar la arquitectura.

Utilizar multiprocessing para cálculo paralelo

Cada proceso posee su propio intérprete y su propio GIL:

from concurrent.futures import ProcessPoolExecutor


def calculate_sum(limit):
    return sum(number * number for number in range(limit))


if __name__ == "__main__":
    limits = [4_000_000] * 4

    with ProcessPoolExecutor() as executor:
        results = list(
            executor.map(calculate_sum, limits)
        )

    print(results)

Este enfoque puede utilizar varios núcleos, pero tiene costes de creación de procesos, serialización y transferencia de datos. No es automáticamente mejor para trabajos pequeños.

ThreadPoolExecutor para I/O

from concurrent.futures import ThreadPoolExecutor
from urllib.request import urlopen


def download_size(url):
    with urlopen(url, timeout=10) as response:
        return len(response.read())


urls = [
    "https://example.com",
    "https://example.org",
]

with ThreadPoolExecutor(max_workers=4) as executor:
    sizes = list(executor.map(download_size, urls))

print(sizes)

Los hilos resultan apropiados porque gran parte del tiempo se dedica a esperar datos de red.

Asyncio como alternativa

asyncio ejecuta muchas tareas cooperativas en un hilo cuando las operaciones admiten espera asíncrona:

import asyncio


async def simulated_request(name):
    await asyncio.sleep(1)
    return f"{name} completada"


async def main():
    results = await asyncio.gather(
        simulated_request("A"),
        simulated_request("B"),
        simulated_request("C"),
    )
    print(results)


asyncio.run(main())

La guía de asyncio en Python explica coroutines, tareas, await y manejo de errores.

Bibliotecas que liberan el GIL

Muchas bibliotecas escritas en C, C++ o Fortran liberan el GIL durante operaciones intensivas. NumPy, por ejemplo, puede ejecutar cálculos internos fuera del bytecode de Python:

import numpy as np

values = np.arange(10_000_000, dtype=float)
result = np.sqrt(values).sum()
print(result)

Esto no significa que todas las funciones de una biblioteca liberen el GIL. Consulta la documentación y mide el caso concreto.

La guía de análisis de datos con NumPy y Pandas muestra operaciones vectorizadas que evitan bucles de Python.

Medir antes de optimizar

No atribuyas cualquier lentitud al GIL. El problema puede ser un algoritmo ineficiente, demasiada entrada y salida, llamadas repetidas, estructuras inadecuadas o consultas lentas.

python -m cProfile -s cumulative app.py

La guía de cProfile en Python explica cómo encontrar las funciones que realmente consumen tiempo.

Ejemplo comparativo

from concurrent.futures import (
    ProcessPoolExecutor,
    ThreadPoolExecutor,
)
from time import perf_counter


def cpu_task(limit):
    total = 0
    for number in range(limit):
        total += number * number
    return total


def measure(executor_class, workers=4):
    start = perf_counter()

    with executor_class(max_workers=workers) as executor:
        list(executor.map(cpu_task, [3_000_000] * workers))

    return perf_counter() - start


if __name__ == "__main__":
    print(
        "Threads:",
        measure(ThreadPoolExecutor),
    )
    print(
        "Processes:",
        measure(ProcessPoolExecutor),
    )

El resultado depende del sistema, del número de núcleos y del tamaño de la tarea. No uses una única ejecución como benchmark definitivo.

Compartir datos entre procesos

Los procesos no comparten memoria Python de forma predeterminada. Los argumentos y resultados deben serializarse. Objetos grandes pueden eliminar parte de la mejora.

Reduce la frecuencia de mensajes, procesa lotes y devuelve resultados compactos. Para datos muy grandes, considera memoria compartida, archivos mapeados o bibliotecas especializadas.

Seguridad con procesos en Windows

En Windows es imprescindible proteger el punto de entrada:

if __name__ == "__main__":
    run_parallel_work()

Sin esta condición, cada proceso hijo puede volver a crear procesos repetidamente.

Hilos y condiciones de carrera

El GIL no elimina todos los problemas de sincronización. Una operación compuesta puede intercalarse con otro hilo:

from threading import Lock

lock = Lock()
counter = 0


def increment():
    global counter

    with lock:
        counter += 1

Utiliza locks, colas o estructuras seguras cuando varios hilos modifican estado compartido. La sentencia with en Python también administra bloqueos como context managers.

Python sin GIL y versiones futuras

El proyecto de Python ha trabajado en compilaciones opcionales sin GIL. La PEP 703 describe una ruta para hacer opcional el GIL en CPython. La disponibilidad, compatibilidad y rendimiento dependen de la versión y de las extensiones utilizadas.

No diseñes una aplicación suponiendo que todos los entornos ejecutan una compilación sin GIL. Comprueba siempre la plataforma concreta.

Elegir la herramienta correcta

  • Utiliza ejecución secuencial para trabajos pequeños y simples.
  • Utiliza threads para espera de red, archivos o servicios.
  • Utiliza asyncio para muchas operaciones asíncronas compatibles.
  • Utiliza procesos para CPU intensiva escrita en Python.
  • Utiliza NumPy u otras bibliotecas nativas para cálculo vectorizado.
  • Utiliza un sistema de colas para trabajos distribuidos o duraderos.

Errores frecuentes

  • Afirmar que Python no puede usar varios núcleos.
  • Crear hilos para acelerar cualquier bucle.
  • Usar procesos para tareas diminutas.
  • Compartir objetos enormes entre procesos.
  • Confundir concurrencia con paralelismo.
  • Ignorar condiciones de carrera porque existe el GIL.
  • Optimizar sin medir.
  • Suponer que toda biblioteca nativa libera el GIL.

Conclusión

El GIL limita la ejecución simultánea de bytecode de Python dentro de un proceso tradicional de CPython, pero no impide concurrencia, procesos paralelos ni cálculo nativo. Clasifica primero la tarea como CPU-bound o I/O-bound. Después mide y elige threads, asyncio, multiprocessing o una biblioteca vectorizada según el cuello de botella real.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Configuración TOML en un proyecto Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    tomllib en Python: lee archivos TOML

    Aprende a leer TOML con tomllib en Python, validar configuraciones, gestionar errores y organizar proyectos con pyproject.toml.

    Ler mais

    Tempo de leitura: 6 minutos
    24/07/2026
    Desarrollador monitoreando registros estructurados en Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Observabilidad en Python con structlog

    Aprende observabilidad en Python con structlog, eventos JSON, contexto, pruebas, control de volumen y recomendaciones para producción.

    Ler mais

    Tempo de leitura: 5 minutos
    24/07/2026
    Configuración segura de aplicaciones Python con Pydantic Settings
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Pydantic Settings: configuración segura

    Aprende a validar variables de entorno, organizar configuraciones y proteger secretos en proyectos Python con Pydantic Settings.

    Ler mais

    Tempo de leitura: 5 minutos
    23/07/2026
    Desenvolvedor programando em Python com Ruff para lint e formatação
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Ruff: lint y formato de código Python

    Aprende Ruff en Python para lint, formato, correcciones automáticas, pyproject.toml, VS Code y CI con una configuración práctica.

    Ler mais

    Tempo de leitura: 10 minutos
    22/07/2026
    Dicas para melhorar performance de scripts Python lentos
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    ¿Por qué Python es lento? Causas y optimización

    Descubre por qué Python puede ser lento y mejora su rendimiento con cProfile, algoritmos, sets, generadores, NumPy, caché y concurrencia.

    Ler mais

    Tempo de leitura: 5 minutos
    12/07/2026
    Leitura segura de senhas no terminal usando Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    getpass: lee contraseñas en terminal con Python

    Lee contraseñas de forma segura con getpass, valida entradas, evita logs y texto plano y almacena credenciales con hashing adecuado.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026