“多线程”嵌入式python中的指针行为

时间:2018-08-08 17:07:35

标签: python c++ python-3.x boost-python python-embedding

这可能很难回答,因为它对我们的体系结构是半特定的,但是我的挖掘使我相信潜在的问题本质上是通用的。

我有几个使用Boost.Python嵌入了Python的DLL。这些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内部:在调用Python之前和之后,赋予RunScript函数的指针都可以正确了解自身,因此发生的一切都在解释器内部。

有趣的是,如果我在编译之前重命名类文件,而不仅仅是提供给BOOST_PYTHON_MODULE(SimplePythonModule)的名称,则此问题将消失。因此,如果将OurModuleClass重命名为OurModuleClass2并重新编译,则函数内的指针将永远不会混淆其起源。

这里到底发生了什么,有什么方法可以解决,而无需为我要编译的每个独立Python模块更改408查找和替换类名?

0 个答案:

没有答案