停止嵌入式Python

时间:2009-09-14 11:19:15

标签: python process multithreading embedded-language python-c-api

我正在将Python解释器嵌入到C程序中。但是,通过PyRun_SimpleString()运行某些python脚本时,可能会遇到无限循环或执行时间过长。考虑PyRun_SimpleString("while 1: pass");在防止主程序阻塞时我以为我可以在一个线程中运行解释器。

如何停止在线程中运行的嵌入式解释器中执行python脚本而不会终止整个过程?

是否可以将异常传递给解释器?我应该将脚本包装在一些可以收听信号的其他脚本下吗?

PS:我可以在一个单独的进程中运行python,但这不是我想要的 - 除非它是最后的手段......


更新

所以,它现在有效。再次感谢Denis Otkidach!

如果我看到这一点,你必须做两件事:告诉解释器停止,并且在你的PyRun_SimpleString()运行的同一个线程中return -1

要停止,有一些可能性:PyErr_SetString(PyExc_KeyboardInterrupt, "...")PyErr_SetInterrupt() - 第一个可能让Python运行更多指令然后停止,后者立即停止执行。

return -1使用Py_AddPendingCall()将函数调用注入Python执行。文档从2.7和3.1版本开始就提到它,但它也运行在早期的Pythons上(这里是2.6)。从2.7和3.1它也应该是线程安全的,这意味着你可以在不获取GIL(?)的情况下调用它。

所以可以重写下面的例子:

int quit() {
    PyErr_SetInterrupt();
    return -1;
}

3 个答案:

答案 0 :(得分:11)

您可以使用Py_AddPendingCall()添加一个函数引发异常,以便在下一个检查间隔调用(有关详细信息,请参阅sys.setcheckinterval()上的文档)。以下是Py_Exit()调用的示例(这对我有用,但可能不是您需要的),请将其替换为Py_Finalize()PyErr_Set*()之一:

int quit(void *) {
    Py_Exit(0);
}


PyGILState_STATE state = PyGILState_Ensure();
Py_AddPendingCall(&quit, NULL);
PyGILState_Release(state);

这对任何纯python代码都应该足够了。但请注意,一些C函数可以作为单个操作运行一段时间(有一个长时间运行的正则表达式搜索的例子,但我不确定它是否仍然相关)。

答案 1 :(得分:1)

python解释器必须在嵌入程序的单独线程中运行,否则你将永远不会有机会中断解释器。

如果你有这个,也许你可以使用Python API异常调用之一来在解释器中触发异常?见Python C API Exceptions

答案 2 :(得分:0)

我希望能够中断任何正在运行的Python代码块,包括取消无限循环或仅删除一些长时间运行的代码。在我的应用程序中,提交并评估了单线程Python代码。中断请求可以随时出现。这个问题和答案对于帮助我真正实现目标至关重要。

看着2019年的这个问题,这里的解决方案对我不起作用;我尝试以多种方式使用Py_AddPendingCall(&quit, NULL);,但被调用函数从未按预期执行,或者根本没有执行。这导致Python引擎挂起,并在我提交Py_FinalizeEx()时最终崩溃。我终于在这个非常有用的answer中找到了一个类似问题所需的内容。

启动Python引擎后,我使用Python代码中的threading包检索线程ID。我将其解析为c long值并将其保存。

中断功能实际上非常简单:
PyGILState_Ensure()
PyThreadState_SetAsyncExc(threadId, PyExc_Exception)使用我之前保存的线程ID和通用异常类型
PyGILState_Release()