Почему Global Interpreter Lock?


89

Какова именно функция глобальной блокировки интерпретатора Python? Используют ли другие языки, скомпилированные в байт-код, аналогичный механизм?


6
Вы также должны спросить: "Это вообще имеет значение?"
S.Lott

2
Я согласен, я считаю, что теперь это не проблема, потому что в 2.6 был добавлен модуль многопроцессорности, позволяющий программировать с использованием нескольких процессов поточно-подобным образом. docs.python.org/library/multiprocessing.html
монкут,

Ответы:


69

В общем, для решения любой проблемы с безопасностью потоков вам необходимо защитить свои внутренние структуры данных с помощью блокировок. Это можно сделать с различными уровнями детализации.

  • Вы можете использовать детализированную блокировку, когда каждая отдельная структура имеет свою собственную блокировку.

  • Вы можете использовать грубую блокировку, когда одна блокировка защищает все (подход GIL).

У каждого метода есть свои плюсы и минусы. Детальная блокировка обеспечивает больший параллелизм - два потока могут выполняться параллельно, когда они не разделяют никаких ресурсов. Однако существуют гораздо большие административные издержки. Для каждой строки кода может потребоваться установить и снять несколько блокировок.

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

Было предпринято несколько попыток удалить GIL в python, но дополнительные накладные расходы для однопоточных машин обычно были слишком большими. В некоторых случаях даже на многопроцессорных машинах может происходить медленнее из-за конфликта блокировок.

Используют ли другие языки, скомпилированные в байт-код, аналогичный механизм?

Он варьируется, и, вероятно, его не следует рассматривать как свойство языка, а скорее как свойство реализации. Например, существуют реализации Python, такие как Jython и IronPython, которые используют поточный подход своей базовой виртуальной машины, а не подход GIL. Кроме того, следующая версия Ruby, похоже, будет продвигаться к введению GIL.


1
Вы можете объяснить это: «Два потока не могут работать одновременно»? Недавно я написал простой веб-сервер на Python с многопоточностью. Для каждого нового запроса от клиента серверы порождают для него новый поток, и этот поток продолжает выполняться. Значит, одновременно будет работать несколько потоков? Или я неправильно понял?
avi

1
@avi AFAIK потоки python не могут работать одновременно, но это не значит, что один поток должен блокировать другой. GIL означает только то, что только один поток может интерпретировать код Python одновременно, это не означает, что управление потоками и распределение ресурсов не работают.
Benproductions1

2
^ поэтому в любой момент времени только один поток будет обслуживать контент для клиента ... поэтому нет смысла использовать многопоточность для повышения производительности. верно?
avi

И, конечно же, Java скомпилирована в байтовый код и допускает очень мелкую блокировку.
Уоррен Дью

3
@avi, процесс, связанный с вводом-выводом, такой как веб-сервер, все еще может получать выгоду от потоков Python. Два или более потока могут выполнять ввод-вывод одновременно. Они просто не могут быть интерпретированы (CPU) одновременно.
Саиш

33

Следующее взято из официального Справочного руководства Python / C API :

Интерпретатор Python не является полностью потокобезопасным. Для поддержки многопоточных программ Python существует глобальная блокировка, которая должна удерживаться текущим потоком, прежде чем он сможет безопасно обращаться к объектам Python. Без блокировки даже самые простые операции могут вызвать проблемы в многопоточной программе: например, когда два потока одновременно увеличивают счетчик ссылок одного и того же объекта, счетчик ссылок может увеличиваться только один раз, а не дважды.

Следовательно, существует правило, согласно которому только поток, получивший глобальную блокировку интерпретатора, может работать с объектами Python или вызывать функции API Python / C. Для поддержки многопоточных программ Python интерпретатор регулярно снимает и повторно устанавливает блокировку - по умолчанию каждые 100 инструкций байт-кода (это можно изменить с помощью sys.setcheckinterval ()). Блокировка также снимается и повторно устанавливается вокруг потенциально блокирующих операций ввода-вывода, таких как чтение или запись файла, так что другие потоки могут работать, пока поток, запрашивающий ввод-вывод, ожидает завершения операции ввода-вывода.

Я думаю, что это довольно хорошо резюмирует проблему.


1
Я тоже читал, но не могу понять, чем Python отличается в этом отношении, скажем, от java (не так ли?)
Федерико А. Рампони

Потоки @EliBendersky Python реализованы как потоки pthread и обрабатываются ОС ( dabeaz.com/python/UnderstandingGIL.pdf ), тогда как потоки Java - это потоки уровня приложения, планирование которых обрабатывается JVM
gokul_uf

19

Глобальная блокировка интерпретатора - это большая блокировка типа мьютекса, которая защищает счетчики ссылок от перехвата. Если вы пишете чистый код Python, все это происходит за кулисами, но если вы встраиваете Python в C, вам, возможно, придется явно снять / снять блокировку.

Этот механизм не связан с компиляцией Python в байт-код. Для Java это не нужно. Фактически, это даже не нужно для Jython (python, скомпилированный в jvm).

см. также этот вопрос


4
«Этот механизм не имеет отношения к компиляции Python в байт-код»: Точнее, это артефакт реализации CPython. Другие реализации (например, Jython, о котором вы упомянули) могут быть свободны от этого ограничения в силу их поточно-ориентированной реализации
Эли Бендерский

11

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

Отдельные потоки Python совместно выполняют многозадачность самим интерпретатором, время от времени циклически переключая блокировку.

Самостоятельно захватить блокировку необходимо, когда вы разговариваете с Python из C, когда другие потоки Python активны, чтобы «выбрать» этот протокол и убедиться, что за вашей спиной ничего небезопасного не происходит.

Другие системы, унаследованные от однопоточных, которые позже превратились в многопоточные системы, часто имеют какой-либо механизм такого рода. Например, ядро ​​Linux имеет «Big Kernel Lock» с первых дней SMP. Постепенно по мере того, как производительность многопоточности становится проблемой, появляется тенденция пытаться разбивать такие блокировки на более мелкие части или заменять их алгоритмами без блокировок и структурами данных, где это возможно, для максимизации пропускной способности.


+1 за упоминание того факта, что используется грубая блокировка, чем многие думают, особенно часто забываемый BKL (я использую reiserfs- единственная реальная причина, по которой я вообще знаю об этом).
new123456 07

3
В Linux был BKL, с версии 2.6.39 BKL полностью удален.
avi

5
Конечно. Имейте в виду, что это было примерно через 3 года после того, как я ответил на вопрос. =)
Эдвард КМЕТТ

7

Что касается вашего второго вопроса, не все языки сценариев используют это, но это только делает их менее мощными. Например, потоки в Ruby зеленые и неродные.

В Python потоки являются собственными, и GIL только предотвращает их запуск на разных ядрах.

В Perl потоки еще хуже. Они просто копируют весь интерпретатор и далеко не так удобны, как в Python.


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.