Урок курса

GIL в Python: потоки, процессы и производительность

Python для профессионалов

GIL — global interpreter lock в обычной сборке CPython. Он ограничивает одновременное выполнение Python-байткода: в один момент его исполняет один поток внутри процесса. Это упрощает часть внутреннего управления памятью интерпретатора, но влияет на масштабирование CPU-bound кода потоками.

GIL не означает, что потоки бесполезны. Во время многих блокирующих операций ввода-вывода и внутри части нативных библиотек блокировка освобождается. Поэтому несколько потоков могут эффективно обслуживать сетевые запросы или файлы, пока другие ждут.

Также GIL не является общей блокировкой ваших данных. Составные операции над бизнес-состоянием всё равно требуют lock, queue или другого механизма синхронизации.

CPU-bound и I/O-bound задачи

Если код большую часть времени вычисляет в чистом Python, два потока обычно не дают двукратного ускорения. Для использования нескольких ядер подходят multiprocessing, ProcessPoolExecutor или библиотека, которая выполняет тяжёлую работу в нативном коде и освобождает GIL.

Если задача в основном ждёт сеть, базу данных или диск, можно использовать threading, ThreadPoolExecutor либо asyncio — выбор зависит от доступных библиотек и архитектуры приложения. Потоки удобны вокруг синхронных API, asyncio — при большом числе неблокирующих соединений.

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

Как принимать решение

Сначала измерьте профиль, а не угадывайте по слову «Python». NumPy, сжатие или обработка изображений могут проводить значительную часть времени вне Python-байткода. Затем проверьте, где программа ждёт и где вычисляет.

Короткая карта выбора:

  • много синхронного I/O — потоки;
  • много асинхронного I/O — asyncio;
  • тяжёлый чистый Python на нескольких ядрах — процессы;
  • векторные вычисления — нативные библиотеки и профилирование;
  • распределённая нагрузка — очередь задач или отдельные сервисы.

Современные реализации Python развиваются, но переносимый код не должен рассчитывать на конкретный экспериментальный режим интерпретатора. Проверяйте поведение той версии и сборки, которую реально запускаете.

Задача 1

Какой подход обычно подходит для тяжёлого CPU-bound кода на чистом Python и нескольких ядрах?

Выберите один вариант
Без регистрации · результат не сохраняется

Продолжить с сохранением прогресса

Перейдите к уроку в учебном кабинете.

Перейти к интерактивному уроку