这可能很难回答,因为它对我们的体系结构是半特定的,但是我的挖掘使我相信潜在的问题本质上是通用的。
我有几个使用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查找和替换类名?