Зачем CPython нужен GIL
https://www.youtube-nocookie.com/embed/AWX4JnAnjBE
Автор: Григорий Петров · MoscowPython
О GIL часто говорят как об одном большом запрете: Python якобы не умеет работать с потоками. Из такого объяснения невозможно понять ни то, почему ограничение появилось, ни то, почему сетевые программы всё же получают пользу от потоков, ни то, какие ошибки GIL предотвращает на самом деле.
Григорий Петров начинает с истории процессов и потоков, а затем постепенно подводит к устройству CPython. Потоки делят память одного процесса, поэтому интерпретатор должен сохранять целостность своих внутренних структур, даже если операционная система переключит выполнение в неудобный момент. GIL даёт право исполнять Python bytecode одному потоку за раз и тем самым упрощает защиту состояния интерпретатора.
Но действие этого ограничения уже, чем кажется. Во время многих блокирующих операций ввода-вывода GIL освобождается, поэтому другие потоки могут продолжать работу. Нативные библиотеки также могут выполнять тяжёлые вычисления вне интерпретатора без удержания GIL. Главное историческое ограничение относится к CPU-bound коду, который исполняет Python bytecode и пытается использовать несколько ядер с помощью потоков.
Важно для 2026 года: доклад описывает стандартную модель CPython с GIL, которая по-прежнему используется по умолчанию. В CPython 3.13 появилась экспериментальная free-threaded сборка, а в Python 3.14 она стала официально поддерживаемой, но осталась опциональной. Поэтому ограничение CPU-bound потоков уже не абсолютно, однако совместимость расширений, накладные расходы и корректность многопоточного кода всё ещё требуют отдельной проверки.
Кому смотреть: Python-разработчикам, которым нужен не очередной совет «используйте процессы», а связная модель того, зачем существует GIL и где проходит граница его влияния.
Из этого можно взять в работу: перед выбором потоков, процессов или async определить, где задача проводит время: в Python bytecode, нативных вычислениях или ожидании ввода-вывода.
Доклад ценен своей последовательностью. Петров начинает не с внутренних структур CPython, а с причин появления процессов и потоков. Процессы дают изоляцию памяти, но усложняют обмен данными. Потоки упрощают совместную работу внутри одной программы, зато делают состояние общим и допускают переключение выполнения между отдельными шагами логической операции.
На этом фоне становится понятнее назначение GIL. Он не является универсальной защитой многопоточной программы и не делает её бизнес-операции атомарными. Его задача — не допустить одновременного исполнения Python bytecode несколькими потоками внутри одного интерпретатора и тем самым защитить внутреннее состояние классического CPython.
Из этого следует важное разграничение. GIL способен сохранить целостность интерпретатора, но не логический инвариант приложения. Один поток может проверить значение, уступить выполнение, а затем продолжить работу после того, как другой поток это значение изменил. Race condition, deadlock и нарушение порядка операций никуда не исчезают. Для состояния приложения по-прежнему нужны явные средства синхронизации и архитектура, которая ограничивает совместное изменение данных.
С другой стороны, наличие GIL не делает все потоки бесполезными. Когда поток ждёт сеть, файл или другую блокирующую операцию, CPython обычно может освободить GIL и дать работать другому потоку. Нативные расширения способны поступать так же во время длительных вычислений. Поэтому один и тот же механизм по-разному влияет на веб-клиент, численную библиотеку и цикл, написанный на чистом Python.
Практическая рекомендация доклада соответствует этой модели: использовать потоки для подходящих операций ввода-вывода, отдавать тяжёлые вычисления библиотекам, которые выполняют их вне Python bytecode, а для независимой CPU-bound работы рассматривать несколько процессов. Обсуждение greenlets и событийного ввода-вывода дополнительно показывает, что конкурентность не сводится к одному выбору между потоком и процессом.
Free-threaded CPython меняет одну важную границу. В специальной сборке Python-потоки могут параллельно исполняться на разных ядрах, включая CPU-bound код. В Python 3.14 этот вариант уже не считается экспериментом, но ещё не стал сборкой по умолчанию. Кроме того, некоторые нативные расширения пока могут снова включать GIL при импорте.
Поэтому доклад не стоит смотреть как описание вечного запрета. Это объяснение архитектурного компромисса, который до сих пор определяет работу стандартного CPython. А free threading добавляет к нему новую главу: глобальный замок постепенно становится опциональным, но цена корректного доступа к общей памяти никуда не исчезает.
Original post: https://territory.network/@engineers-tail/164/zachem-cpython-nuzhen-gil