GIL de Python: qué es y cómo afecta al rendimiento

Publicado el: 11/07/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

    Dicas para otimizar scripts Python lentos
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Por qué tu script Python es lento y cómo mejorarlo

    Descubre por qué un script Python es lento y cómo mejorarlo con cProfile, timeit, estructuras correctas, generadores y mejores algoritmos.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Leitura de variáveis de ambiente em projetos Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Variables de entorno en Python: guía segura

    Aprende a usar variables de entorno en Python, archivos .env, validación, conversión de tipos, secretos, pruebas y buenas prácticas.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Introdução ao uso de type hints em Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Type hints en Python: escribe código más claro

    Aprende type hints en Python con funciones, colecciones, Optional, TypedDict, Callable, genéricos, Protocol y herramientas de análisis.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Medição de tempo de execução de código Python com timeit
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Timeit en Python: medir el rendimiento correctamente

    Aprende timeit en Python para comparar funciones, repetir benchmarks, separar setup, medir por operación y evitar errores de microoptimización.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Criação de cliente TCP simples usando Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Cliente TCP en Python con sockets: guía práctica

    Crea un cliente TCP en Python con sockets: conexión, bytes, framing, timeouts, errores, reintentos, protocolos, seguridad y código reutilizable.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Configuração de logs em aplicações Python usando logging
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Logging en Python: guía completa para principiantes

    Aprende logging en Python: niveles, archivos, formatos, excepciones, handlers, rotación, loggers por módulo y buenas prácticas.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026