Документация Python неофициальный перевод
Содержание страницы

concurrent.interpreters – Несколько интерпретаторов в одном процессеconcurrent.interpreters – Multiple interpreters in the same process

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

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


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

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

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

См. также

InterpreterPoolExecutor

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

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

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

PEP 554

PEP 734

PEP 684

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

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

Ключевые деталиKey details

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

  • изолированы, по умолчанию

  • нет неявных потоков

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

ВведениеIntroduction

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

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

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

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

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

Примечание

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

Выполнение в интерпретатореRunning in an Interpreter

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

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

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

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

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

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

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

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

На высоком уровне можно рассматривать комбинацию потоков и интерпретаторов как потоки с возможностью совместного использования по желанию.

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

Взаимодействие между интерпретаторамиCommunication Between Interpreters

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

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

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

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

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

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

СсылкаReference

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

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.

Объекты InterpreterInterpreter objects

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)

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

ИсключенияExceptions

exception concurrent.interpreters.InterpreterError

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

exception concurrent.interpreters.InterpreterNotFoundError

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

exception concurrent.interpreters.ExecutionFailed

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

excinfo

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

exception concurrent.interpreters.NotShareableError

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

Взаимодействие между интерпретаторамиCommunicating Between Interpreters

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(), когда очередь полна.

Базовое использованиеBasic usage

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

from concurrent import interpreters

interp = interpreters.create()

# Выполнить в текущем потоке ОС.

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)

# Выполнить в новом потоке ОС.

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