从 C 调用 Py_Finalize()

Posted

技术标签:

【中文标题】从 C 调用 Py_Finalize()【英文标题】:Calling Py_Finalize() from C 【发布时间】:2009-09-15 12:51:46 【问题描述】:

这是对Call Python from C++的跟进

在程序启动时,我调用以下函数来初始化解释器:

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() 时遇到了段错误。

void exitPython()
    PyEval_AcquireLock();
    Py_Finalize();

关于 Py_Finalize() 的引用相当可疑(或者我可能只是以错误的方式阅读它)并且我不确定如果有一些活动线程 PyEval_AcquireLock() 是否可以获取锁以及如果有会发生什么调用 Py_Finalize() 时是活动线程。

无论如何,即使我确定所有线程都已完成其工作,我也会遇到段错误,但前提是至少创建了一个。例如。在 exitPython() 之后调用 initPython() 不会产生错误。

我可以忽略这个问题,并希望操作系统知道它做了什么,但如果我能弄清楚发生了什么,我会更喜欢..

【问题讨论】:

好问题。如果你有时间,也许你可以帮我解决一个类似的问题:***.com/questions/59959303/… 【参考方案1】:

是的,整个部分都很可疑,但我想我弄错了。

我必须在初始化解释器时保存 PyThreadState 并在我完成它时交换这个状态(不知道为什么我需要一个特定的 ThreadState 来调用 Finalize - 不应该每个状态都工作吗?)

无论如何,如果其他人遇到同样的问题:

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() 会发生什么。听起来像是竞态条件的完美示例..

【讨论】:

【参考方案2】:

您是否尝试过注释掉您线程中完成的所有“工作”?用繁忙的循环或睡眠或其他东西代替它。这将有助于确定它是您的初始化/关闭代码,还是您在两者之间对 Python 实际执行的操作。也许您没有正确设置线程 - C API 中有很多特定于线程的函数,我不确定您需要哪些函数来确保正确操作。

【讨论】:

除了 PyGILState_Ensure() 和 Release() 之外,我已经将所有内容都注释掉了,但错误仍然存​​在。如果我也将它们注释掉,就没有问题.. 在那种情况下,我猜线程管理有些事情没有正确完成。不幸的是,包含所有线程函数的相关 C API 页面对于您需要哪些调用远非显而易见。 “你需要的电话”大多数时候是Py_BEGIN_ALLOW_THREADS 和它的对应物。反过来,这些是使用PyEval_SaveThread() 及其对应物的宏。所以如果我写的是像 OP 这样的东西,我会按照那个例子。【参考方案3】:

在通过嵌入式解释器从不同线程运行包含pyxhook 的脚本时,我也遇到了类似的问题。

如果一次运行一个脚本没有问题。挂钩已正确释放,但如果两个或多个脚本并行运行,则挂钩不会停止。尽管我的脚本正确返回并且来自pyxhookcancel() 也正确返回,但我认为仍然有一些与xlib 相关的线程正在运行。这个pyxhook 问题我已经解决了,方法是保留一个全局标志来观察pyxhook 是否已经在运行,而不是从每个线程重新初始化pyxhook

现在关于Py_Finalize(),如果pyxhook 在每个线程中重新初始化:

如果我在调用Py_Finalize() 之前没有调用PyEval_AcquireLock()PyThreadState_Swap(),它会在Linux 中终止,但在Win32 中不会。在Win32中如果我不经过PyEval_AcquireLock()PyThreadState_Swap()会有问题。

暂时对我来说临时解决方案是在两个不同的操作系统中以不同的方式终止。

【讨论】:

以上是关于从 C 调用 Py_Finalize()的主要内容,如果未能解决你的问题,请参考以下文章

Py_Initialize 和 Py_finalize 和 MatPlotlib

嵌入式 Python 应用程序中 Py_Finalize 期间的致命错误

Python 3 解释器在嵌入时是不是会泄漏内存?

c可以调用python吗

Omnet ++简单模块的C ++代码中python嵌入代码中的分段错误错误

从 finally 块调用回调函数时出现问题