Почему Глобальная Блокировка Интерпретатора?
Какова именно функция Python Global Interpreter Lock? Используют ли другие языки, скомпилированные для байт-кода, аналогичный механизм?
6 ответов
В общем, для любой проблемы безопасности потоков вам нужно будет защитить внутренние структуры данных с помощью блокировок. Это может быть сделано с различными уровнями детализации.
Вы можете использовать мелкозернистую блокировку, где каждая отдельная структура имеет свою собственную блокировку.
Вы можете использовать грубую блокировку, когда одна блокировка защищает все (подход GIL).
Есть разные плюсы и минусы каждого метода. Мелкозернистая блокировка обеспечивает больший параллелизм - два потока могут выполняться параллельно, если они не совместно используют ресурсы. Однако административные издержки значительно выше. Для каждой строки кода вам может потребоваться получить и снять несколько блокировок.
Грубый подход противоположен. Два потока не могут работать одновременно, но отдельный поток будет работать быстрее, потому что он не делает так много бухгалтерии. В конечном итоге все сводится к компромиссу между однопоточной скоростью и параллелизмом.
Было несколько попыток удалить GIL в python, но дополнительные издержки для однопоточных машин, как правило, были слишком большими. Некоторые случаи могут быть медленнее даже на многопроцессорных компьютерах из-за конфликта блокировок.
Используют ли другие языки, скомпилированные для байт-кода, аналогичный механизм?
Он варьируется, и, вероятно, его не следует рассматривать как свойство языка, а как свойство реализации. Например, есть реализации Python, такие как Jython и IronPython, которые используют многопоточный подход своей базовой виртуальной машины, а не подход GIL. Кроме того, следующая версия Ruby, похоже, движется к внедрению GIL.
Ниже приводится официальное справочное руководство по API Python/C:
Интерпретатор Python не является полностью потокобезопасным. Для поддержки многопоточных программ Python существует глобальная блокировка, которая должна удерживаться текущим потоком, прежде чем он сможет безопасно получить доступ к объектам Python. Без блокировки даже самые простые операции могут вызвать проблемы в многопоточной программе: например, когда два потока одновременно увеличивают счетчик ссылок одного и того же объекта, счетчик ссылок может увеличиваться только один раз, а не дважды.
Поэтому существует правило, согласно которому только поток, получивший глобальную блокировку интерпретатора, может работать с объектами Python или вызывать функции API Python/C. Чтобы поддерживать многопоточные программы на Python, интерпретатор регулярно снимает и снова блокирует блокировку - по умолчанию каждые 100 инструкций байт-кода (это можно изменить с помощью sys.setcheckinterval()). Блокировка также снимается и повторно запрашивается вокруг потенциально блокирующих операций ввода-вывода, таких как чтение или запись файла, так что другие потоки могут работать, пока поток, который запрашивает ввод-вывод, ожидает завершения операции ввода-вывода.
Я думаю, что это подводит итог проблемы довольно хорошо.
Глобальная блокировка интерпретатора - это большая блокировка типа мьютекса, которая защищает счетчики ссылок от попадания. Если вы пишете чистый код Python, все это происходит за кулисами, но если вы встраиваете Python в C, вам, возможно, придется явно снять / снять блокировку.
Этот механизм не связан с тем, что Python компилируется в байт-код. Это не нужно для Java. На самом деле, это даже не нужно для Jython (python скомпилирован в jvm).
см. также этот вопрос
Python, как и Perl 5, не был разработан с нуля, чтобы быть потокобезопасным. После этого были созданы потоки, поэтому глобальная блокировка интерпретатора используется для поддержания взаимного исключения, когда только один поток выполняет код в определенный момент времени в недрах интерпретатора.
Отдельные потоки Python совместно выполняют многозадачность самим интерпретатором, периодически повторяя блокировку.
Самостоятельно захватывать блокировку необходимо, когда вы разговариваете с Python из C, когда другие потоки Python активны, чтобы подписаться на этот протокол и убедиться, что за вашей спиной не происходит ничего небезопасного.
Другие системы, которые имеют однопотоковое наследие, которое впоследствии превратилось в многопоточные системы, часто имеют такой механизм. Например, ядро Linux имеет "Big Kernel Lock" с первых дней SMP. Постепенно со временем, когда производительность многопоточности становится проблемой, возникает тенденция разбивать эти виды блокировок на более мелкие части или заменять их алгоритмами без блокировки и структурами данных, где это возможно, для максимизации пропускной способности.
Что касается вашего второго вопроса, не все языки сценариев используют это, но это только делает их менее мощными. Например, потоки в Ruby являются зелеными, а не нативными.
В Python потоки являются родными, и GIL только запрещает им работать на разных ядрах.
В Perl темы еще хуже. Они просто копируют весь интерпретатор и далеко не так удобны, как в Python.