Содержание страницы
Несколько интерпретаторов в одном процессе Python¶Multiple interpreters in a Python process
Хотя в большинстве случаев достаточно встроить один интерпретатор Python, бывают ситуации, когда требуется создать несколько независимых интерпретаторов в одном процессе и даже в одном потоке. Под-интерпретаторы позволяют это сделать.
«Главный» интерпретатор – это первый интерпретатор, создаваемый при инициализации среды выполнения.
Обычно он является единственным интерпретатором Python в процессе. В отличие от под-интерпретаторов, главный интерпретатор обладает уникальными общепроцессными обязанностями, такими как обработка сигналов. Он также отвечает за выполнение при инициализации среды выполнения и обычно является активным интерпретатором при завершении работы среды выполнения. Функция PyInterpreterState_Main() возвращает указатель на его состояние.
Переключаться между под-интерпретаторами можно с помощью функции PyThreadState_Swap().
Создавать и удалять их можно с помощью следующих функций:
-
type PyInterpreterConfig¶
Структура, содержащая большинство параметров для настройки под-интерпретатора. Её значения используются только в
Py_NewInterpreterFromConfig()и никогда не изменяются средой выполнения.Добавлено в версии 3.12.
Поля структуры:
-
int use_main_obmalloc¶
Если это
0, то под-интерпретатор будет использовать собственное состояние аллокатора «объектов». В противном случае он будет использовать (разделять) состояние главного интерпретатора.Если это
0, тоcheck_multi_interp_extensionsдолжен быть1(ненулевым). Если это1, тоgilне должен бытьPyInterpreterConfig_OWN_GIL.
-
int allow_fork¶
Если это
0, то среда выполнения не будет поддерживать создание форка процесса в любом потоке, где в данный момент активен под-интерпретатор. В противном случае форк не ограничен.Обратите внимание, что модуль
subprocessпо-прежнему работает, когда форк запрещён.
-
int allow_exec¶
Если это
0, то среда выполнения не будет поддерживать замену текущего процесса через exec (например,os.execv()) в любом потоке, где в данный момент активен под-интерпретатор. В противном случае exec не ограничен.Обратите внимание, что модуль
subprocessпо-прежнему работает, когда exec запрещён.
-
int allow_threads¶
Если это
0, то модульthreadingпод-интерпретатора не будет создавать потоки. В противном случае потоки разрешены.
-
int allow_daemon_threads¶
Если это
0, то модульthreadingпод-интерпретатора не будет создавать потоки-демоны. В противном случае потоки-демоны разрешены (при условии, чтоallow_threadsне равен нулю).
-
int check_multi_interp_extensions¶
Если это
0, то все модули расширений могут быть импортированы, включая устаревшие модули (с однофазной инициализацией), в любом потоке, где активен под-интерпретатор. В противном случае могут быть импортированы только модули расширений с многофазной инициализацией (см. PEP 489). (См. такжеPy_mod_multiple_interpreters.)Это должно быть
1(ненулевым), еслиuse_main_obmallocравно0.
-
int gil¶
Это определяет режим работы GIL для под-интерпретатора. Он может быть одним из следующих:
-
PyInterpreterConfig_DEFAULT_GIL¶
Использовать выбор по умолчанию (
PyInterpreterConfig_SHARED_GIL).
-
PyInterpreterConfig_SHARED_GIL¶
Использовать (разделять) GIL главного интерпретатора.
-
PyInterpreterConfig_OWN_GIL¶
Использовать собственный GIL под-интерпретатора.
Если это
PyInterpreterConfig_OWN_GIL, тоPyInterpreterConfig.use_main_obmallocдолжен быть0.-
PyInterpreterConfig_DEFAULT_GIL¶
-
int use_main_obmalloc¶
-
PyStatus Py_NewInterpreterFromConfig(PyThreadState **tstate_p, const PyInterpreterConfig *config)¶
Создаёт новый под-интерпретатор. Это (почти) полностью независимое окружение для выполнения кода Python. В частности, новый интерпретатор имеет отдельные, независимые версии всех импортированных модулей, включая фундаментальные модули
builtins,__main__иsys. Таблица загруженных модулей (sys.modules) и путь поиска модулей (sys.path) также отдельные. В новом окружении нет переменнойsys.argv. У него есть новые файловые объекты стандартных потоков ввода-выводаsys.stdin,sys.stdoutиsys.stderr(однако они ссылаются на те же нижележащие файловые дескрипторы).Переданный config управляет параметрами, с которыми инициализируется интерпретатор.
В случае успеха tstate_p будет установлен в первое состояние потока, созданное в новом под-интерпретаторе. Это состояние потока присоединено. Обратите внимание, что при этом не создаётся реальный поток; см. обсуждение состояний потока ниже. Если создание нового интерпретатора не удалось, tstate_p устанавливается в
NULL; исключение не устанавливается, так как состояние исключения хранится в присоединённом состоянии потока, которое может не существовать.Как и для всех остальных функций Python/C API, перед вызовом этой функции должно присутствовать присоединённое состояние потока, но после возврата оно может быть отсоединено. В случае успеха возвращённое состояние потока будет присоединено. Если под-интерпретатор создаётся с собственным GIL, то присоединённое состояние потока вызывающего интерпретатора будет отсоединено. Когда функция возвращается, состояние потока нового интерпретатора будет присоединено к текущему потоку, а присоединённое состояние потока предыдущего интерпретатора останется отсоединённым.
Добавлено в версии 3.12.
Под-интерпретаторы наиболее эффективны при изоляции друг от друга, с ограничением определённой функциональности:
PyInterpreterConfig config = { .use_main_obmalloc = 0, .allow_fork = 0, .allow_exec = 0, .allow_threads = 1, .allow_daemon_threads = 0, .check_multi_interp_extensions = 1, .gil = PyInterpreterConfig_OWN_GIL, }; PyThreadState *tstate = NULL; PyStatus status = Py_NewInterpreterFromConfig(&tstate, &config); if (PyStatus_Exception(status)) { Py_ExitStatusException(status); }
Обратите внимание, что config используется только кратковременно и не изменяется. Во время инициализации значения config преобразуются в различные значения
PyInterpreterState. Копия config только для чтения может быть сохранена внутри наPyInterpreterState.Модули расширения совместно используются (под-)интерпретаторами следующим образом:
Для модулей, использующих многофазную инициализацию, например
PyModule_FromDefAndSpec(), для каждого интерпретатора создаётся и инициализируется отдельный объект модуля. Между этими объектами модулей совместно используются только статические и глобальные переменные на уровне C.Для модулей, использующих устаревшую однофазную инициализацию, например
PyModule_Create(), при первом импорте конкретного расширения оно инициализируется обычным образом, а (поверхностная) копия его словаря модуля сохраняется. Когда то же расширение импортируется другим (под-)интерпретатором, создаётся новый модуль и заполняется содержимым этой копии; функцияinitрасширения не вызывается. Объекты в словаре модуля таким образом оказываются совместно используемыми между (под-)интерпретаторами, что может привести к нежелательному поведению (см. Ошибки и предостережения ниже).Обратите внимание, что это отличается от ситуации, когда расширение импортируется после полной повторной инициализации интерпретатора вызовом
Py_FinalizeEx()иPy_Initialize(); в этом случае функцияinitmoduleрасширения вызывается снова. Как и при многофазной инициализации, это означает, что между этими модулями совместно используются только статические и глобальные переменные на уровне C.
-
PyThreadState *Py_NewInterpreter(void)¶
- Часть Stable ABI.
Создаёт новый под-интерпретатор. По сути, это просто обёртка вокруг
Py_NewInterpreterFromConfig()с config, который сохраняет существующее поведение. Результатом является неизолированный под-интерпретатор, который разделяет GIL главного интерпретатора, допускает fork/exec, разрешает фоновые потоки и допускает модули с однофазной инициализацией.
-
void Py_EndInterpreter(PyThreadState *tstate)¶
- Часть Stable ABI.
Уничтожает (под-)интерпретатор, представленный заданным состоянием потока. Заданное состояние потока должно быть присоединено. После возврата вызова не будет присоединённого состояния потока. Все состояния потоков, связанные с этим интерпретатором, уничтожаются.
Py_FinalizeEx()уничтожит все под-интерпретаторы, которые не были явно уничтожены к этому моменту.
Индивидуальный GIL для каждого интерпретатора¶A per-interpreter GIL
Добавлено в версии 3.12.
С помощью Py_NewInterpreterFromConfig() можно создать
под-интерпретатор, который полностью изолирован от других интерпретаторов,
включая собственный GIL. Самое важное преимущество такой
изоляции в том, что такой интерпретатор может выполнять код Python без
блокировки другими интерпретаторами и не блокируя другие. Таким образом,
один процесс Python может по-настоящему использовать несколько ядер CPU
при выполнении кода Python. Изоляция также поощряет иной подход к параллелизму,
отличный от простого использования потоков.
(См. PEP 554 и PEP 684.)
Использование изолированного интерпретатора требует бдительности для сохранения этой
изоляции. Это особенно означает, что нельзя совместно использовать объекты или изменяемое
состояние без гарантий потокобезопасности. Даже объекты, которые в остальном
неизменяемы (например, None, (1, 5)), обычно не могут быть использованы совместно
из-за счётчика ссылок. Один простой, но менее эффективный подход
– использовать глобальную блокировку при любом использовании некоторого состояния (или объекта).
С другой стороны, эффективно неизменяемые объекты (такие как целые числа или строки)
можно сделать безопасными, несмотря на их счётчики ссылок, сделав их бессмертными.
Фактически, это уже сделано для встроенных синглтонов, маленьких целых чисел
и ряда других встроенных объектов.
Если сохранять изоляцию, то будет доступ к настоящим многоядерным вычислениям без осложнений, связанных со свободной многопоточностью. Несоблюдение изоляции приведёт ко всем последствиям свободной многопоточности, включая гонки и трудноотлаживаемые крахи.
Кроме того, одна из главных проблем использования нескольких изолированных интерпретаторов – как безопасно (не нарушая изоляцию) и эффективно общаться между ними. Среда выполнения и стандартная библиотека пока не предоставляют стандартного подхода для этого. Будущий модуль стандартной библиотеки поможет уменьшить усилия по сохранению изоляции и предоставит эффективные инструменты для обмена (и совместного использования) данными между интерпретаторами.
Ошибки и предостережения¶Bugs and caveats
Поскольку под-интерпретаторы (и основной интерпретатор) являются частью одного
процесса, изоляция между ними не идеальна – например, используя
низкоуровневые файловые операции, такие как os.close(), они могут
(случайно или злонамеренно) влиять на открытые файлы друг друга. Из-за того,
как расширения совместно используются между (под-)интерпретаторами, некоторые расширения могут
работать неправильно; это особенно вероятно при использовании однофазной инициализации
или (статических) глобальных переменных.
Можно вставить объекты, созданные в одном под-интерпретаторе, в
пространство имён другого (под-)интерпретатора; этого следует избегать по возможности.
Следует проявлять особую осторожность, чтобы избежать совместного использования пользовательских функций, методов, экземпляров или классов между под-интерпретаторами, поскольку операции импорта, выполняемые такими объектами, могут повлиять на словарь загруженных модулей не того (под-)интерпретатора. Не менее важно избегать совместного использования объектов, из которых доступны вышеперечисленные.
Также обратите внимание, что комбинирование этой функциональности с API PyGILState_*
является деликатным, поскольку эти API предполагают взаимно однозначное соответствие между состояниями потоков Python
и потоками уровня ОС, а это предположение нарушается наличием под-интерпретаторов.
Настоятельно рекомендуется не переключать под-интерпретаторы между парой
соответствующих вызовов PyGILState_Ensure() и PyGILState_Release().
Более того, расширения (такие как ctypes), использующие эти API для вызова
кода Python из потоков, созданных не в Python, вероятно, будут работать неправильно при использовании
под-интерпретаторов.
Высокоуровневые API¶High-level APIs
-
type PyInterpreterState¶
- Часть Stable ABI (как непрозрачная структура).
Эта структура данных представляет состояние, совместно используемое несколькими взаимодействующими потоками. Потоки, принадлежащие одному интерпретатору, разделяют администрирование модулей и несколько других внутренних элементов. В этой структуре нет открытых членов.
Потоки, принадлежащие разным интерпретаторам, изначально не разделяют ничего, кроме состояния процесса, такого как доступная память, открытые файловые дескрипторы и т.п. Глобальная блокировка интерпретатора также разделяется всеми потоками, независимо от того, какому интерпретатору они принадлежат.
Изменено в версии 3.12: PEP 684 ввёл возможность индивидуального GIL для каждого интерпретатора. См.
Py_NewInterpreterFromConfig().
-
PyInterpreterState *PyInterpreterState_Get(void)¶
- Часть Stable ABI начиная с версии 3.9.
Получает текущий интерпретатор.
Вызывает фатальную ошибку, если присоединённое состояние потока отсутствует. Функция не может вернуть NULL.
Добавлено в версии 3.9.
-
int64_t PyInterpreterState_GetID(PyInterpreterState *interp)¶
- Часть Stable ABI начиная с версии 3.7.
Возвращает уникальный идентификатор интерпретатора. Если при этом произошла ошибка, возвращается
-1и устанавливается исключение.Вызывающий должен иметь прикреплённое состояние потока.
Добавлено в версии 3.7.
-
PyObject *PyInterpreterState_GetDict(PyInterpreterState *interp)¶
- Возвращаемое значение: заимствованная ссылка. Часть Stable ABI начиная с версии 3.8.
Возвращает словарь, в котором могут храниться данные, специфичные для интерпретатора. Если эта функция возвращает
NULL, исключение не было возбуждено, и вызывающий код должен считать, что словарь данных интерпретатора недоступен.Это не замена
PyModule_GetState(), который расширения должны использовать для хранения информации о состоянии интерпретатора.Возвращаемый словарь заимствуется из интерпретатора и действителен до завершения работы интерпретатора.
Добавлено в версии 3.8.
-
typedef PyObject *(*_PyFrameEvalFunction)(PyThreadState *tstate, _PyInterpreterFrame *frame, int throwflag)¶
Тип функции вычисления фрейма.
Параметр throwflag используется методом
throw()генераторов: если он не равен нулю, обрабатывается текущее исключение.Изменено в версии 3.9: Теперь функция принимает параметр tstate.
Изменено в версии 3.11: Параметр frame изменился с
PyFrameObject*на_PyInterpreterFrame*.
-
_PyFrameEvalFunction _PyInterpreterState_GetEvalFrameFunc(PyInterpreterState *interp)¶
Возвращает функцию вычисления фрейма.
См. PEP 523 «Добавление API вычисления фрейма в CPython».
Добавлено в версии 3.9.
-
void _PyInterpreterState_SetEvalFrameFunc(PyInterpreterState *interp, _PyFrameEvalFunction eval_frame)¶
Устанавливает функцию вычисления фрейма.
См. PEP 523 «Добавление API вычисления фрейма в CPython».
Добавлено в версии 3.9.
-
void _PyInterpreterState_SetEvalFrameAllowSpecialization(PyInterpreterState *interp, int allow_specialization)¶
Включает или отключает специализацию, когда установлен собственный вычислитель фреймов.
Если allow_specialization не равен нулю, адаптивный специализатор продолжит специализировать байт-коды, даже если установлена пользовательская функция вычисления фрейма. Когда allow_specialization равен нулю, установка пользовательского вычислителя отключает специализацию. Стандартный цикл интерпретатора будет продолжать деоптимизацию, пока действует API вычисления фрейма – функция вычисления фрейма должна обрабатывать специализированные опкоды, чтобы воспользоваться этим.
Добавлено в версии 3.15.
-
int _PyInterpreterState_IsSpecializationEnabled(PyInterpreterState *interp)¶
Возвращает ненулевое значение, если для интерпретатора включена адаптивная специализация. Специализация включена, когда не установлена пользовательская функция вычисления фрейма, или когда она установлена с включённым allow_specialization.
Добавлено в версии 3.15.
Низкоуровневые API¶Low-level APIs
Все следующие функции должны вызываться после Py_Initialize().
Изменено в версии 3.7: Py_Initialize() теперь инициализирует GIL
и устанавливает присоединённое состояние потока.
-
PyInterpreterState *PyInterpreterState_New()¶
- Часть Stable ABI.
Создаёт новый объект состояния интерпретатора. Присоединённое состояние потока не требуется, но может опционально существовать, если необходимо сериализовать вызовы этой функции.
Возбуждает событие аудита
cpython.PyInterpreterState_Newбез аргументов.
-
void PyInterpreterState_Clear(PyInterpreterState *interp)¶
- Часть Stable ABI.
Сбрасывает всю информацию в объекте состояния интерпретатора. Для интерпретатора должно существовать присоединённое состояние потока.
Возбуждает событие аудита
cpython.PyInterpreterState_Clearбез аргументов.
-
void PyInterpreterState_Delete(PyInterpreterState *interp)¶
- Часть Stable ABI.
Уничтожает объект состояния интерпретатора. Для целевого интерпретатора не должно быть прикреплённого состояния потока. Состояние интерпретатора должно быть сброшено предыдущим вызовом
PyInterpreterState_Clear().
Поддержка расширенной отладки¶Advanced debugger support
Эти функции предназначены только для использования расширенными инструментами отладки.
-
PyInterpreterState *PyInterpreterState_Head()¶
Возвращает объект состояния интерпретатора, находящийся в начале списка всех таких объектов.
-
PyInterpreterState *PyInterpreterState_Main()¶
Возвращает объект состояния основного интерпретатора.
-
PyInterpreterState *PyInterpreterState_Next(PyInterpreterState *interp)¶
Возвращает следующий объект состояния интерпретатора после interp из списка всех таких объектов.
-
PyThreadState *PyInterpreterState_ThreadHead(PyInterpreterState *interp)¶
Возвращает указатель на первый объект
PyThreadStateв списке потоков, связанных с интерпретатором interp.
-
PyThreadState *PyThreadState_Next(PyThreadState *tstate)¶
Возвращает следующий объект состояния потока после tstate из списка всех таких объектов, принадлежащих тому же объекту
PyInterpreterState.