GIL и free-threading (PEP 703)

Материал из llmdocs — справочник разработчика
Портал: Python
GIL и free-threading (PEP 703)
GILмьютекс, разрешающий выполнять байт-код Python только одному потоку
Появиласьв CPython с поддержкой потоков (1992)
PEP 703Сэм Гросс (Meta), принят в 2023 году
Python 3.13экспериментальная сборка без GIL (python3.13t)
Python 3.14сборка без GIL официально поддерживается (PEP 779)
Альтернативыmultiprocessing, субинтерпретаторы, asyncio, расширения на C и Rust

GIL (англ. Global Interpreter Lock — глобальная блокировка интерпретатора) — механизм в CPython, основной реализации Python, который разрешает выполнять байт-код Python только одному потоку в каждый момент времени. Даже на 16-ядерном процессоре несколько потоков Python, занятых вычислениями, работают по очереди, а не одновременно. Free-threading («свободная многопоточность») — сборка CPython без GIL, ставшая возможной благодаря PEP 703: в ней потоки выполняют Python-код по-настоящему параллельно.[1]

Зачем был нужен GIL

CPython управляет памятью с помощью подсчёта ссылок: у каждого объекта есть счётчик, и объект удаляется, когда счётчик падает до нуля. Если два потока одновременно меняют счётчик без синхронизации, он испортится — объект удалится раньше времени или никогда. GIL, появившийся в 1992 году вместе с поддержкой потоков, решал это просто: один замок на весь интерпретатор. Это давало быстрый однопоточный код, простую реализацию и лёгкое написание расширений на C, которые могли не думать о потокобезопасности.

Цена — невозможность ускорить вычисления потоками. Попытки убрать GIL предпринимались с конца 1990-х, но каждая замедляла однопоточные программы, и их отвергали.

Когда GIL мешает, а когда нет

Задача Влияние GIL
Ожидание сети, диска, базы данных почти не мешает: на время ввода-вывода GIL отпускается
Вычисления в NumPy, PyTorch, сжатие, хеширование не мешает: код на C отпускает GIL на время работы
Вычисления на чистом Python в потоках мешает: потоки выполняются по очереди

Поэтому для веб-серверов и парсеров потоки и asyncio работают хорошо, а для тяжёлых вычислений на Python традиционно использовали процессы (см. Конкурентность и параллелизм).

PEP 703

В 2021–2023 годах Сэм Гросс из Meta показал прототип CPython без GIL с приемлемыми потерями производительности. Ключевые приёмы:

  • Смещённый подсчёт ссылок (biased reference counting): поток-владелец меняет счётчик объекта быстро, без атомарных операций, а остальные потоки — через отдельный атомарный счётчик.
  • Бессмертные объекты: у None, True, малых чисел счётчик не меняется вовсе.
  • Аллокатор mimalloc и блокировки на уровне отдельных объектов вместо одной глобальной.

В октябре 2023 года руководящий совет Python принял PEP 703 с поэтапным планом: сначала экспериментальная сборка, затем официальная поддержка и, если экосистема будет готова, — переход по умолчанию.

Версия Статус
3.13 (2024) экспериментальная сборка без GIL, заметное замедление однопоточного кода[4]
3.14 (2025) официально поддерживаемая сборка (PEP 779), замедление сокращено[2]
будущее возможный переход на сборку без GIL по умолчанию

Как попробовать

uv python install 3.14t                 # сборка без GIL (суффикс t — threaded)
uv run --python 3.14t python -c "import sys; print(sys._is_gil_enabled())"   # False
import threading, time

def work(n: int) -> int:
    return sum(i * i for i in range(n))

start = time.perf_counter()
threads = [threading.Thread(target=work, args=(10_000_000,)) for _ in range(4)]
for t in threads: t.start()
for t in threads: t.join()
print(f"{time.perf_counter() - start:.1f} с")
# с GIL — примерно как 4 последовательных вызова; без GIL — заметно быстрее на 4 ядрах

Если в процесс загружается расширение на C, не объявившее поддержку free-threading, интерпретатор по умолчанию включает GIL обратно и предупреждает об этом; переменная окружения PYTHON_GIL=0 позволяет принудительно отключить его.[3]

Что меняется для разработчиков

  • Гонки данных становятся реальностью. Код, «случайно» корректный благодаря GIL, может сломаться: общий счётчик, словарь-кэш, ленивая инициализация требуют блокировок (threading.Lock) или потокобезопасных структур (см. Состояние гонки и блокировки). Встроенные типы — list, dict — защищены от повреждения, но последовательность операций над ними атомарной не становится.
  • Расширения на C и Rust должны быть проверены и пересобраны под новую сборку; сообщество отслеживает готовность популярных пакетов.[6]
  • Однопоточный код в сборке без GIL работает немного медленнее.

Альтернативы

  • multiprocessing и ProcessPoolExecutor — отдельные процессы, каждый со своим GIL; надёжно, но данные приходится сериализовать.
  • Субинтерпретаторы — несколько интерпретаторов в одном процессе, каждый со своим GIL (PEP 684, Python 3.12);[5] в Python 3.14 появился модуль concurrent.interpreters.
  • asyncio — для задач с ожиданием ввода-вывода GIL не помеха.
  • Вынос горячего кода в NumPy, Numba, Cython или расширения на Rust, отпускающие GIL.

См. также

Источники

  1. PEP 703 — Making the Global Interpreter Lock Optional in CPython
  2. PEP 779 — Criteria for supported status for free-threaded Python
  3. Python HOWTO — Python support for free threading
  4. What's New In Python 3.13
  5. PEP 684 — A Per-Interpreter GIL
  6. Python free-threading guide (py-free-threading.github.io)