Интерпретатор Python как класс C++
-
18-09-2019 - |
Вопрос
Я работаю над внедрением Python в С++.В каком-то необычном случае мне нужны два отдельных экземпляра интерпретатора в одном потоке.
Могу ли я включить интерпретатор Python в класс C++ и получать услуги от двух или более экземпляров класса?
Решение
Я использовал Py_NewInterpreter для разных интерпретаторов в разных потоках, но это также должно работать для нескольких интерпретаторов в одном потоке:
В основной теме:
Py_Initialize();
PyEval_InitThreads();
mainThreadState = PyEval_SaveThread();
Для каждого экземпляра интерпретатора (в любом потоке):
// initialize interpreter
PyEval_AcquireLock(); // get the GIL
myThreadState = Py_NewInterpreter();
... // call python code
PyEval_ReleaseThread(myThreadState); // swap out thread state + release the GIL
... // any other code
// continue with interpreter
PyEval_AcquireThread(myThreadState); // get GIL + swap in thread state
... // call python code
PyEval_ReleaseThread(myThreadState);
... // any other code
// finish with interpreter
PyEval_AcquireThread(myThreadState);
... // call python code
Py_EndInterpreter(myThreadState);
PyEval_ReleaseLock(); // release the GIL
Обратите внимание, что вам нужна переменная myThreadState для каждого экземпляра интерпретатора!
Наконец завершение основной темы:
PyEval_RestoreThread(mainThreadState);
Py_Finalize();
Существуют некоторые ограничения на использование нескольких экземпляров интерпретатора (они не кажутся полностью независимыми), но в большинстве случаев это не вызывает проблем.
Другие советы
Вызывать Py_Initialize()
дважды, однако, это не сработает Py_NewInterpreter
может работать, в зависимости от того, что вы пытаетесь сделать.Внимательно прочитайте документацию, при вызове необходимо удерживать GIL.
Вы можете, но я бы рекомендовал вам не переопределять интерпретатор Python, если существует стандартная реализация.Использовать повышение::питон для взаимодействия с Python.
Я не думаю, что вы первый, кто захочет это сделать, к сожалению, я считаю, что это невозможно.Можете ли вы запускать интерпретаторы Python как отдельные процессы и использовать RPC?
Ответ mosaik не сработал в моей ситуации, когда мой модуль представляет собой плагин для хост-приложения, которое уже инициализирует Python.Мне удалось заставить его работать с помощью следующего кода.
// initialize interpreter
::PyEval_InitThreads();
::PyThreadState *mainThread = ::PyThreadState_Get();
myState = ::Py_NewInterpreter();
... // call python code
::PyThreadState_Swap(mainThread);
... // any other code
mainThread = ::PyThreadState_Swap(myState)
... // call python code
::PyThreadState_Swap(mainThread)
... // any other code
// finished with interpreter
mainThread = ::PyThreadState_Swap(myState)
::Py_EndInterpreter(myState);
::PyThreadState_Swap(mainThread)
Когда я позвонил PyEval_AcquireLock()
программа заблокировалась и функция не вернулась.Далее, вызывая PyEval_ReleaseThread(myState)
казалось, лишило законной силы и переводчика.
- Вы можете позволить интерпретатору Python жить за пределами памяти вашего приложения.Просто встройте интерпретатор в DLL.
- Вы можете настроить и сохранить контексты Python для имитации двух разных интерпретаторов.