Вопрос с собеседования · Python

Что такое GIL?

JuniorКонкурентность
Ещё спрашивают так:
  • Зачем в Python глобальная блокировка интерпретатора?
  • Почему потоки в Python не ускоряют вычисления?
  • Можно ли обойти GIL?
Коротко

GIL — блокировка в CPython: байткод Python в один момент выполняет только один поток.

  • CPU-задачи потоки не ускоряют, задачи ввода-вывода — ускоряют: GIL отпускается на ожидании
  • Для вычислений — процессы (multiprocessing) или C-расширения, отпускающие GIL (NumPy)

А ещё С Python 3.13 есть отдельная сборка без GIL (PEP 703), по умолчанию GIL остаётся

Подробно

GIL (Global Interpreter Lock) — мьютекс внутри CPython. Поток должен держать его, чтобы выполнять байткод. Он упрощает управление памятью: подсчёт ссылок на объекты не нужно защищать отдельными блокировками, а C-расширения проще писать.

Что из этого следует:

  • вычисления (CPU-bound) в нескольких потоках идут не быстрее, чем в одном: потоки по очереди получают GIL;
  • ввод-вывод (сеть, диск, ожидание базы) — потоки помогают: на время ожидания поток отпускает GIL, и работают другие;
  • многие C-библиотеки (NumPy, обработка изображений, сжатие) отпускают GIL на время своих вычислений и параллелятся в потоках.

Как обойти для вычислений: multiprocessing или concurrent.futures.ProcessPoolExecutor — у каждого процесса свой интерпретатор и свой GIL; вынести тяжёлое в C, Cython или Rust.

GIL не делает код потокобезопасным: операции вида x += 1 состоят из нескольких шагов байткода, и поток может прерваться посередине. Общие данные всё равно защищают Lock.

GIL — особенность CPython. Начиная с Python 3.13 есть экспериментальная сборка без GIL (free-threaded, PEP 703), но стандартная сборка работает с GIL.

Пример

import threading
from concurrent.futures import ProcessPoolExecutor

def cpu_task(n):
    return sum(i * i for i in range(n))

# Потоки: CPU-задача не ускорится
threads = [threading.Thread(target=cpu_task, args=(10_000_000,)) for _ in range(4)]

# Процессы: настоящий параллелизм на нескольких ядрах
with ProcessPoolExecutor() as pool:
    results = list(pool.map(cpu_task, [10_000_000] * 4))

Что хотят услышать

  • Что такое GIL и в какой реализации он есть.
  • Разница влияния на CPU-bound и I/O-bound задачи.
  • Способы обойти: процессы, C-расширения.
  • GIL не заменяет блокировки для общих данных.

Частые ошибки

  • «В Python потоки вообще бесполезны» — для ввода-вывода они работают.
  • «GIL делает код потокобезопасным».

Проверьте себя

Один верный вариант. Подумайте, потом откройте ответ.

Показать варианты и ответ
  • ✗GIL не позволяет запускать больше одного потока, поэтому threading в CPython бесполезен даже для сети
  • ✗Потоки ускоряют CPU-задачи благодаря GIL: он распределяет байткод между ядрами
  • ✗GIL есть во всех реализациях Python, включая Jython и IronPython, — это часть языка
  • ✓Блокировка в CPython: байткод в один момент выполняет только один поток

Что спросят дальше

Разбор темы

Связанные вопросы

Партнёр платформы

Разбор собеседования с sotto

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования