从C调用Py_Finalize()
这是对从C++调用Python问题的后续讨论。
在程序启动时,我调用以下函数来初始化解释器:
void initPython(){
PyEval_InitThreads();
Py_Initialize();
PyEval_ReleaseLock();
}
每个线程都会创建自己的数据结构,并通过以下方式获取锁:
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
//call python API, process results
PyGILState_Release(gstate);
一旦你理解了全局解释器锁(GIL),这就相对简单了,但问题是当我调用Py_Finalize()时会出现段错误(segfault)。
void exitPython(){
PyEval_AcquireLock();
Py_Finalize();
}
关于Py_Finalize()的参考资料有点模糊(或者可能是我理解错了),我不确定如果有活动线程时,PyEval_AcquireLock()是否能成功获取锁,以及在调用Py_Finalize()时如果有活动线程会发生什么。
无论如何,即使我确定所有线程都已经完成工作,只要至少创建了一个线程,我就会遇到段错误。例如,调用initPython()后再调用exitPython()就不会出现错误。
我可以选择忽略这个问题,希望操作系统能处理好,但我更希望能弄清楚到底发生了什么……
3 个回答
我在运行包含 pyxhook
的脚本时,也遇到了类似的问题,这些脚本是通过嵌入式解释器从不同的线程中运行的。
如果一次只运行一个脚本,那就没有问题。这个钩子会正常释放,但如果同时运行两个或更多脚本,钩子就不会停止。虽然我的脚本正常返回,并且 pyxhook
的 cancel()
也正常返回,但我觉得还是有一些与 xlib
相关的线程在运行。为了解决这个 pyxhook
的问题,我设置了一个全局标志来监控 pyxhook
是否已经在运行,并且不在每个线程中重新初始化 pyxhook
。
现在说到 Py_Finalize()
,如果在每个线程中重新初始化 pyxhook
:
如果我在调用 Py_Finalize()
之前不调用 PyEval_AcquireLock()
和 PyThreadState_Swap()
,在 Linux 上会终止,但在 Win32 上就会有问题。如果我不通过 PyEval_AcquireLock()
和 PyThreadState_Swap()
,在 Win32 上会出现问题。
目前对我来说,临时解决方案是在两个不同的操作系统中以不同的方式终止。
你有没有试过把你线程里所有的“工作”都注释掉?可以用一个忙等循环或者让它睡一会儿来替代。这可以帮助你找出问题是出在初始化或关闭代码上,还是在你实际对Python做的事情上。也许你没有正确设置线程——在C语言的API里有很多和线程相关的函数,我不太确定你需要用哪些来确保它们正常运行。
是的,这一整段内容确实有点可疑,不过我想我找到了我的错误。
我需要在初始化解释器的时候保存PyThreadState,然后在结束的时候再把这个状态换回来(我不知道为什么调用Finalize时需要一个特定的ThreadState,难道不是任何状态都可以吗?)
无论如何,如果其他人遇到同样的问题,这里有个例子:
PyThreadState *mainstate;
void initPython(){
PyEval_InitThreads();
Py_Initialize();
mainstate = PyThreadState_Swap(NULL);
PyEval_ReleaseLock();
}
void exitPython(){
PyEval_AcquireLock();
PyThreadState_Swap(mainstate);
Py_Finalize();
}
唯一的问题是,我可以像其他线程一样获取锁,即使还有其他线程在工作。 这个API没有说明当Finalize()被调用时,如果还有其他线程在工作会发生什么。这听起来就像是一个典型的竞争条件的例子……