Spec-Zone.ru › Python 3.14

concurrent.interpreters — Несколько интерпретаторов в одном процессе

Добавлено в версии 3.14.

Исходный код: Lib/concurrent/interpreters

Модуль concurrent.interpreters создаёт интерфейсы более высокого уровня на основе низкоуровневого модуля _interpreters.

Модуль предназначен прежде всего для предоставления базового API для управления интерпретаторами (также называемыми «подинтерпретаторами») и выполнения в них задач. Выполнение в основном подразумевает переключение на интерпретатор (в текущем потоке) и вызов функции в этом контексте выполнения.

Для поддержки параллелизма сами интерпретаторы (и этот модуль) обеспечивают лишь изоляцию, которая сама по себе бесполезна. Реальный параллелизм доступен отдельно через threads — см. ниже.

См. также

InterpreterPoolExecutor

Объединяет потоки с интерпретаторами в привычном интерфейсе.

Изоляция модулей расширения

Как обновить модуль расширения для поддержки нескольких интерпретаторов.

PEP 554

PEP 734

PEP 684

Доступность: недоступен в WASI.

Этот модуль не работает или недоступен в WebAssembly. Дополнительные сведения см. в разделе Платформы WebAssembly.

Основные сведения

Прежде чем перейти к подробностям, обратите внимание на несколько важных моментов, связанных с использованием нескольких интерпретаторов:

  • по умолчанию изолированы
  • не создают потоки неявно
  • пока не все пакеты PyPI поддерживают использование в нескольких интерпретаторах

Введение

«Интерпретатор» — это, по сути, контекст выполнения среды Python. Он содержит всё состояние, необходимое среде выполнения для исполнения программы, включая такие данные, как состояние импорта и встроенные объекты. (У каждого потока, даже если существует только основной поток, есть некоторое дополнительное состояние среды выполнения, помимо текущего интерпретатора, связанное с текущим исключением и циклом вычисления байт-кода.)

Концепция интерпретатора и связанная с ней функциональность присутствуют в Python начиная с версии 2.2, однако эта возможность была доступна только через C-API и была мало известна; кроме того, изоляция изолированных интерпретаторов оставалась относительно неполной вплоть до версии 3.12.

Несколько интерпретаторов и изоляция

Реализация Python может поддерживать использование нескольких интерпретаторов в одном процессе. CPython поддерживает эту возможность. Каждый интерпретатор фактически изолирован от остальных (за исключением небольшого числа тщательно контролируемых случаев, связанных с глобальным состоянием процесса).

Такая изоляция полезна прежде всего для чёткого разделения отдельных логических компонентов программы, когда необходимо тщательно контролировать их взаимодействие.

Примечание

Интерпретаторы в одном процессе технически никогда не могут быть полностью изолированы друг от друга, поскольку доступ к памяти внутри одного процесса почти ничем не ограничен. Среда выполнения Python прилагает все усилия для обеспечения изоляции, однако модули расширения могут легко её нарушить. Поэтому не используйте несколько интерпретаторов в ситуациях, критичных с точки зрения безопасности, когда они не должны иметь доступа к данным друг друга.

Выполнение в интерпретаторе

Выполнение в другом интерпретаторе подразумевает переключение на него в текущем потоке, а затем вызов некоторой функции. Среда выполнения исполнит эту функцию, используя состояние текущего интерпретатора. Модуль concurrent.interpreters предоставляет базовый API для создания интерпретаторов и управления ими, а также для переключения на интерпретатор и вызова функции.

Для этой операции автоматически не запускаются другие потоки. Однако для этого есть вспомогательная функция. Также предусмотрена отдельная вспомогательная функция для вызова встроенной функции exec() в интерпретаторе.

Когда в интерпретаторе вызывается exec() (или eval()), функция выполняется с использованием модуля __main__ интерпретатора в качестве пространства имён «globals». То же справедливо для функций, не связанных ни с одним модулем. Так же выполняются и скрипты, запускаемые из командной строки: они работают в модуле __main__.

Конкурентность и параллелизм

Как отмечалось выше, сами по себе интерпретаторы не обеспечивают конкурентность. Они представляют собой исключительно изолированный контекст выполнения, который среда использует в текущем потоке. Благодаря такой изоляции они похожи на процессы, но при этом, подобно потокам, сохраняют эффективность внутрипроцессного взаимодействия.

Тем не менее интерпретаторы естественным образом поддерживают некоторые разновидности конкурентности. Изоляция даёт важный побочный эффект: она позволяет использовать иной подход к конкурентности, отличный от асинхронного программирования и потоков. Эта модель конкурентности похожа на CSP или модель акторов и относительно проста для понимания.

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

У каждого потока Python, даже если выполняется только основной поток, есть собственный текущий контекст выполнения. Несколько потоков могут использовать один и тот же интерпретатор или разные интерпретаторы.

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

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

Обмен данными между интерпретаторами

На практике несколько интерпретаторов полезны только в том случае, если между ними можно обмениваться данными. Обычно это подразумевает передачу сообщений, но иногда может означать и совместное использование данных определённым тщательно контролируемым способом.

С учётом этого модуль concurrent.interpreters предоставляет реализацию queue.Queue, доступную через create_queue().

«Совместное использование» объектов

Любые данные, которыми фактически обмениваются интерпретаторы, лишаются потокобезопасности, обеспечиваемой GIL. Для решения этой проблемы в модулях расширения есть различные способы. Однако в коде Python отсутствие потокобезопасности означает, что объекты нельзя фактически использовать совместно, за некоторыми исключениями. Вместо этого необходимо создать копию, поэтому изменяемые объекты не будут синхронизированы.

По умолчанию большинство объектов при передаче в другой интерпретатор копируются с помощью pickle. Почти все неизменяемые встроенные объекты либо непосредственно используются совместно, либо эффективно копируются. Например:

  • None
  • bool (True и False)
  • bytes
  • str
  • int
  • float
  • tuple (состоящие из объектов, поддержка которых аналогична)

Существует небольшое число типов Python, изменяемые данные которых фактически используются совместно несколькими интерпретаторами:

  • memoryview
  • Queue

Справочник

Этот модуль определяет следующие функции:

concurrent.interpreters.list_all()

Возвращает list объектов Interpreter — по одному для каждого существующего интерпретатора.

concurrent.interpreters.get_current()

Возвращает объект Interpreter для текущего выполняющегося интерпретатора.

concurrent.interpreters.get_main()

Возвращает объект Interpreter для основного интерпретатора. Это интерпретатор, созданный средой выполнения для запуска REPL или скрипта, переданного в командной строке. Обычно он единственный.

concurrent.interpreters.create()

Инициализирует новый (неактивный) интерпретатор Python и возвращает объект Interpreter для него.

concurrent.interpreters.create_queue()

Инициализирует новую межинтерпретаторную очередь и возвращает объект Queue для неё.

Объекты интерпретатора

class concurrent.interpreters.Interpreter(id)

Один интерпретатор в текущем процессе.

Как правило, Interpreter не следует вызывать напрямую. Вместо этого используйте create() или одну из других функций модуля.

id

(только для чтения)

Идентификатор базового интерпретатора.

whence

(только для чтения)

Строка, описывающая происхождение интерпретатора.

is_running()

Возвращает True, если интерпретатор в данный момент выполняет код в своём модуле __main__, и False в противном случае.

close()

Завершает работу интерпретатора и уничтожает его.

prepare_main(ns=None, **kwargs)

Привязывает объекты в модуле __main__ интерпретатора.

Некоторые объекты фактически используются совместно, а некоторые эффективно копируются, но большинство копируется с помощью pickle. См. раздел «Совместное использование» объектов.

exec(code, /, dedent=True)

Выполняет указанный исходный код в интерпретаторе (в текущем потоке).

call(callable, /, *args, **kwargs)

Возвращает результат вызова указанной функции в интерпретаторе (в текущем потоке).

call_in_thread(callable, /, *args, **kwargs)

Выполняет указанную функцию в интерпретаторе (в новом потоке).

Исключения

exception concurrent.interpreters.InterpreterError

Это исключение, подкласс Exception, возникает при ошибке, связанной с интерпретатором.

exception concurrent.interpreters.InterpreterNotFoundError

Это исключение, подкласс InterpreterError, возникает, если целевой интерпретатор больше не существует.

exception concurrent.interpreters.ExecutionFailed

Это исключение, подкласс InterpreterError, возникает, если выполняемый код вызвал необработанное исключение.

excinfo

Краткая копия сведений об исключении, возникшем в другом интерпретаторе.

exception concurrent.interpreters.NotShareableError

Это исключение, подкласс TypeError, возникает, если объект нельзя отправить в другой интерпретатор.

Обмен данными между интерпретаторами

class concurrent.interpreters.Queue(id)

Обёртка над низкоуровневой межинтерпретаторной очередью, реализующая интерфейс queue.Queue. Базовую очередь можно создать только с помощью create_queue().

Некоторые объекты фактически используются совместно, а некоторые эффективно копируются, но большинство копируется с помощью pickle. См. раздел «Совместное использование» объектов.

id

(только для чтения)

Идентификатор очереди.

exception concurrent.interpreters.QueueEmptyError

Это исключение, подкласс queue.Empty, возникает в функциях Queue.get() и Queue.get_nowait(), если очередь пуста.

exception concurrent.interpreters.QueueFullError

Это исключение, подкласс queue.Full, возникает в функциях Queue.put() и Queue.put_nowait(), если очередь заполнена.

Основы использования

Создание интерпретатора и выполнение в нём кода:

from concurrent import interpreters

interp = interpreters.create()

# Run in the current OS thread.

interp.exec('print("spam!")')

interp.exec("""if True:
    print('spam!')
    """)

from textwrap import dedent
interp.exec(dedent("""
    print('spam!')
    """))

def run(arg):
    return arg

res = interp.call(run, 'spam!')
print(res)

def run():
    print('spam!')

interp.call(run)

# Run in new OS thread.

t = interp.call_in_thread(run)
t.join()

© 2001 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.14/library/concurrent.interpreters.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API