Несколько интерпретаторов в процессе Python
Хотя в большинстве случаев встраивается только один интерпретатор 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. -
-
-
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); }Обратите внимание: конфигурация используется только на короткое время и не изменяется. Во время инициализации значения конфигурации преобразуются в различные значения
PyInterpreterState. Копия конфигурации, доступная только для чтения, может храниться внутриPyInterpreterState.Модули расширений совместно используются (суб-)интерпретаторами следующим образом:
- Для модулей с многофазной инициализацией, например
PyModule_FromDefAndSpec(), для каждого интерпретатора создаётся и инициализируется отдельный объект модуля. Между этими объектами модулей совместно используются только статические переменные и глобальные переменные уровня C. -
Для модулей с устаревшей однофазной инициализацией, например
PyModule_Create(), при первом импорте конкретного расширения оно инициализируется обычным образом, а (поверхностная) копия словаря модуля сохраняется. Когда то же расширение импортируется другим (суб-)интерпретатором, создаётся новый модуль и заполняется содержимым этой копии; функцияinitрасширения не вызывается. Таким образом, объекты в словаре модуля оказываются общими для (суб-)интерпретаторов, что может привести к нежелательному поведению (см. раздел Ошибки и предостережения ниже).Обратите внимание: это отличается от ситуации, когда расширение импортируется после полной повторной инициализации интерпретатора вызовами
Py_FinalizeEx()иPy_Initialize(); в этом случае функцияinitmoduleрасширения вызывается снова. Как и при многофазной инициализации, это означает, что между этими модулями совместно используются только статические переменные и глобальные переменные уровня C.
- Для модулей с многофазной инициализацией, например
-
PyThreadState *Py_NewInterpreter(void) -
Часть стабильного ABI.
Создаёт новый субинтерпретатор. По сути, это просто оболочка над
Py_NewInterpreterFromConfig()с конфигурацией, сохраняющей существующее поведение. В результате создаётся неизолированный субинтерпретатор, который использует GIL главного интерпретатора совместно с ним, разрешает создание ответвлений и exec, разрешает потоки-демоны и модули с однофазной инициализацией.
-
void Py_EndInterpreter(PyThreadState *tstate) -
Часть стабильного ABI.
Уничтожает (суб-)интерпретатор, представленный переданным состоянием потока. Переданное состояние потока должно быть присоединено. После возврата вызова не останется ни одного присоединённого состояния потока. Все состояния потоков, связанные с этим интерпретатором, уничтожаются.
Py_FinalizeEx()уничтожит все субинтерпретаторы, которые к этому моменту не были уничтожены явно.
Отдельный GIL для каждого интерпретатора
Добавлено в версии 3.12.
С помощью Py_NewInterpreterFromConfig() можно создать субинтерпретатор, полностью изолированный от других интерпретаторов, в том числе с собственным GIL. Главное преимущество такой изоляции состоит в том, что такой интерпретатор может выполнять код Python, не блокируясь другими интерпретаторами и не блокируя их. Таким образом, один процесс Python может в полной мере использовать несколько ядер ЦП при выполнении кода Python. Изоляция также способствует иному подходу к параллелизму, чем простое использование потоков. (См. PEP 554 и PEP 684.)
При использовании изолированного интерпретатора необходимо внимательно следить за сохранением изоляции. В частности, нельзя совместно использовать объекты или изменяемое состояние без гарантий потокобезопасности. Даже объекты, которые в остальном неизменяемы (например, None, (1, 5)), обычно нельзя использовать совместно из-за счётчика ссылок. Один из простых, но менее эффективных способов обойти это ограничение — использовать глобальную блокировку при любом обращении к некоторому состоянию (или объекту). Другой вариант — сделать фактически неизменяемые объекты (например, целые числа или строки) безопасными, несмотря на их счётчики ссылок, превратив их в бессмертные. Именно так поступили со встроенными синглтонами, небольшими целыми числами и рядом других встроенных объектов.
Если сохранять изоляцию, можно будет использовать настоящие многопроцессорные вычисления без сложностей, связанных с отсутствием блокировки потоков. Нарушение изоляции повлечёт за собой все последствия отсутствия блокировки потоков, включая гонки и трудноотлаживаемые сбои.
Помимо этого, одна из главных сложностей использования нескольких изолированных интерпретаторов — безопасная (без нарушения изоляции) и эффективная передача данных между ними. Среда выполнения и стандартная библиотека пока не предоставляют стандартного способа решения этой задачи. Будущий модуль стандартной библиотеки помог бы упростить сохранение изоляции и предоставил бы эффективные средства для передачи (и совместного использования) данных между интерпретаторами.
Ошибки и предостережения
Поскольку субинтерпретаторы (и главный интерпретатор) являются частью одного процесса, изоляция между ними не идеальна — например, с помощью низкоуровневых операций с файлами, таких как os.close(), они могут (случайно или злонамеренно) влиять на открытые файлы друг друга. Из-за особенностей совместного использования расширений (суб-)интерпретаторами некоторые расширения могут работать неправильно; особенно вероятно это при использовании однофазной инициализации или (статических) глобальных переменных. Объекты, созданные в одном субинтерпретаторе, можно поместить в пространство имён другого (суб-)интерпретатора; по возможности этого следует избегать.
Следует проявлять особую осторожность и не допускать совместного использования пользовательских функций, методов, экземпляров или классов между субинтерпретаторами, поскольку операции импорта, выполняемые такими объектами, могут затронуть словарь загруженных модулей не того (суб-)интерпретатора. Не менее важно избегать совместного использования объектов, из которых доступны перечисленные выше объекты.
Также обратите внимание, что сочетать эту функциональность с API PyGILState_* следует осторожно, поскольку эти API предполагают взаимно однозначное соответствие между состояниями потоков Python и потоками на уровне ОС, а наличие субинтерпретаторов нарушает это предположение. Настоятельно рекомендуется не переключать субинтерпретаторы между парными вызовами PyGILState_Ensure() и PyGILState_Release(). Кроме того, расширения (например, ctypes), использующие эти API для вызова кода Python из потоков, созданных не средствами Python, вероятно, будут работать неправильно при использовании субинтерпретаторов.
Высокоуровневые API
-
type PyInterpreterState -
Часть ограниченного API (как непрозрачная структура).
Эта структура данных представляет состояние, общее для нескольких взаимодействующих потоков. Потоки, принадлежащие одному интерпретатору, совместно используют управление модулями и несколько других внутренних элементов. В этой структуре нет общедоступных членов.
Потоки, принадлежащие разным интерпретаторам, изначально не имеют ничего общего, кроме состояния процесса, такого как доступная память, открытые файловые дескрипторы и т. п. Глобальная блокировка интерпретатора также используется совместно всеми потоками независимо от того, к какому интерпретатору они относятся.
Изменено в версии 3.12: PEP 684 добавил возможность использовать отдельный GIL для каждого интерпретатора. См.
Py_NewInterpreterFromConfig().
-
PyInterpreterState *PyInterpreterState_Get(void) -
Часть стабильного ABI с версии 3.9.
Получает текущий интерпретатор.
Вызывает фатальную ошибку, если нет присоединённого состояния потока. Не может вернуть NULL.
Добавлено в версии 3.9.
-
int64_t PyInterpreterState_GetID(PyInterpreterState *interp) -
Часть стабильного ABI с версии 3.7.
Возвращает уникальный идентификатор интерпретатора. Если при этом произошла ошибка, возвращается
-1и устанавливается ошибка.У вызывающего кода должно быть присоединённое состояние потока.
Добавлено в версии 3.7.
-
PyObject *PyInterpreterState_GetDict(PyInterpreterState *interp) -
Возвращаемое значение: заимствованная ссылка.Часть стабильного 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.
Низкоуровневые API
Все перечисленные ниже функции необходимо вызывать после Py_Initialize().
Изменено в версии 3.7: Py_Initialize() теперь инициализирует GIL и устанавливает присоединённое состояние потока.
-
PyInterpreterState *PyInterpreterState_New() -
Часть стабильного ABI.
Создаёт новый объект состояния интерпретатора. Присоединённое состояние потока не требуется, но может существовать, если необходимо сериализовать вызовы этой функции.
Вызывает событие аудита
cpython.PyInterpreterState_Newбез аргументов.
-
void PyInterpreterState_Clear(PyInterpreterState *interp) -
Часть стабильного ABI.
Сбрасывает всю информацию в объекте состояния интерпретатора. Для интерпретатора должно существовать присоединённое состояние потока.
Вызывает событие аудита
cpython.PyInterpreterState_Clearбез аргументов.
-
void PyInterpreterState_Delete(PyInterpreterState *interp) -
Часть стабильного ABI.
Уничтожает объект состояния интерпретатора. Для целевого интерпретатора не должно быть присоединённого состояния потока. Состояние интерпретатора должно быть сброшено предыдущим вызовом
PyInterpreterState_Clear().
Расширенная поддержка отладчиков
Эти функции предназначены только для использования средствами расширенной отладки.
-
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.
© 2001 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.14/c-api/subinterpreters.html