Поведение указателя во "многопоточном" встроенном питоне

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

У меня есть несколько библиотек DLL, которые встроили Python с использованием Boost.Python. Эти DLL загружаются в программу Windows, которая запускает каждую DLL в своем собственном потоке. Когда программа запускает библиотеку DLL, она запускает сценарий, а затем сценарий запускается в цикле, пока ему не будет приказано остановиться. Когда ему говорят об остановке, основной поток зависает в памяти (в ожидании команды "Перезапустить"), но рабочий поток, выполняющий сценарий, корректно завершается и удаляется из памяти. Он многопоточный, так как каждый скрипт технически выполняется в своем собственном потоке, но все они полностью независимы друг от друга и не взаимодействуют.

(Связанное ограничение: всеобъемлющая программа понятия не имеет, что эти модули запускают Python, и модули не могут взаимодействовать друг с другом, и программа не может координировать их взаимодействия Python. Забавно.)

У меня были проблемы с GIL, и все эти потоки втискивались в интерпретатор без сбоев, но я сработал. Получение GIL перед входом в поток заставило всех играть хорошо. Вот код, который запускает скрипт:

BOOST_PYTHON_MODULE(SimplePythonModule)
{
    class_<OurModuleClass, boost::noncopyable, bases<OurBaseModuleClasses>>("OurModuleClass", no_init)
    .def("GetMyName", &OurModuleClass::GetMyName);

    def("LogSomething", &LogSomething); // A program-wide API function
}

int PythonModule::RunScript(OurModuleClass * m_Ptr)
{
    int retval = 0;
    PyGILState_STATE gstate;
    PyThreadState * state;
    Py_Initialize();              // Py3.7 auto-inits threads here
    gstate = PyGILState_Ensure(); // Acquire the lock
    state = Py_NewInterpreter();
    object main_module = object(handle<(borrowed(PyImport_AddModule("__main__"))));
    dict global = extract<dict>(main_module.attr("__dict__"));
    object result = exec_file((LPCTSTR)m_Ptr->m_sScriptName, global, global);
    object runner = global["EntryFunction"];
    retval = extract<int>(runner(boost::python::ptr(m_Ptr))); // Passing the pointer to the script
    PyEval_ReleaseThread(state);
    return retval;
}

Внутри скрипта они выполняют такие вещи, как вызов функции LogSomething всеобъемлющей программы, когда они делают свое дело:

from SimplePythonModule import *


def EntryFunction(ptr):
    while True:
       # ... things happen here ...
       LogSomething(ptr.GetMyName())

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

SimplePythonModule: SimplePythonModule

Но затем все становится странным, когда я компилирую копию этого модуля, изменяя имя, объявленное в BOOST_PYTHON_MODULE (скажем, компиляция с увеличивающимися именами 1-4), и компилирую как отдельные библиотеки DLL с одинаковыми именами. В циклах сначала все будет выглядеть хорошо, каждый печатает свое имя правильно:

SimplePythonModule1: SimplePythonModule1
SimplePythonModule2: SimplePythonModule2
SimplePythonModule3: SimplePythonModule3

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

SimplePythonModule1: SimplePythonModule4

Программа, которая печатает журналы, знает, что это Module1, который вызвал функцию, но указатель в скрипте считает, что он принадлежит к последнему загруженному модулю. Однако он выполняется в файле сценария, назначенном для Module1. Я определил, что это происходит только внутри Python: указатель, данный функции RunScript, знает себя как до, так и после вызова Python, поэтому все, что происходит, находится внутри интерпретатора.

Интересно, что эта проблема исчезнет, ​​если я переименую файлы классов перед компиляцией, а не просто имя, предоставленное BOOST_PYTHON_MODULE(SimplePythonModule). Поэтому, если OurModuleClass переименовывается в OurModuleClass2 и перекомпилируется, указатели внутри функции никогда не запутаются в их происхождении.

Что именно здесь происходит, и есть ли способ исправить это, не требуя 408 изменений имени класса Find and Replace для каждого независимого модуля Python, который я хочу скомпилировать?

0 ответов

Другие вопросы по тегам