Что такое GIL и как он влияет на многопоточность в Python?
Короткий ответ
- GIL — глобальная блокировка интерпретатора в CPython
- В один момент байткод выполняет только один поток
- CPU-bound задачи в потоках не ускоряются
- I/O-bound задачи ускоряются: GIL отпускается на время ожидания
- Для CPU-нагрузки — multiprocessing или C-расширения
- С Python 3.13 есть экспериментальная сборка без GIL (free-threaded)
GIL позволяет лишь одному потоку исполнять байткод, поэтому потоки помогают при I/O, а для CPU-задач нужны процессы или нативные расширения.
Как сказать вслух
пример ответаGIL — это блокировка в CPython, из-за которой байткод в один момент времени выполняет только один поток, даже на многоядерном процессоре. Поэтому потоки не ускоряют вычисления, но отлично подходят для ожидания сети или диска — на время I/O блокировка отпускается. Для тяжёлых вычислений берут multiprocessing или библиотеки вроде numpy, которые считают вне GIL. Ещё упомяну, что начиная с Python 3.13 появилась экспериментальная сборка без GIL.
Подробный ответ
Основной ответ
GIL (Global Interpreter Lock) — мьютекс в CPython, который гарантирует, что байткод исполняет только один поток одновременно. Он упрощает управление памятью (подсчёт ссылок становится безопасным без мелких блокировок), но лишает потоки параллелизма на CPU. При I/O-операциях (сокеты, файлы, sleep) поток отпускает GIL, поэтому threading хорошо подходит для сетевых задач. CPU-bound код распараллеливают через multiprocessing (отдельные процессы со своими интерпретаторами) или через нативные расширения (numpy, Cython), которые освобождают GIL. В Python 3.13+ появилась официальная free-threaded сборка (PEP 703), где GIL отключаем, но она пока не является дефолтом.
Ключевые моменты
- Зачем GIL вообще нужен. Он защищает внутренние структуры интерпретатора и счётчики ссылок, делая однопоточный код быстрым и простым.
- I/O-bound vs CPU-bound. Выбор инструмента зависит от типа нагрузки: потоки и asyncio для ожидания, процессы для вычислений.
- Обходные пути. multiprocessing, concurrent.futures.ProcessPoolExecutor, C-расширения и numpy выполняют работу вне GIL.
- Актуальное состояние. PEP 703: free-threaded CPython доступен с 3.13 как отдельная сборка, экосистема постепенно адаптируется.
Практический контекст
Вопрос встречается почти на каждом собеседовании по Python. Интервьюер смотрит, может ли кандидат правильно выбрать инструмент: threading/asyncio для сетевых сервисов и парсеров, ProcessPoolExecutor для обработки данных. Частое продолжение — «почему потоки всё же полезны» и «что изменится без GIL». В реальной работе это выбор между воркерами uvicorn, пулом процессов для отчётов и очередями задач вроде Celery.
Частые ошибки
- Утверждают, что потоки в Python «вообще бесполезны», забывая про I/O-bound задачи
- Путают GIL с особенностью языка: это деталь CPython, в Jython или free-threaded сборке его нет
- Думают, что GIL делает любой код потокобезопасным — гонки на уровне логики всё равно возможны