26

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

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

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

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


更新:

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

如果我没看错,您必须做两件事:告诉解释器停止并return -1在 PyRun_SimpleString() 运行的同一线程中。

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

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

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

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

3 回答 3

12

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

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


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

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

于 2009-09-15T14:21:33.163 回答
2

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

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

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

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

于 2019-05-15T00:28:07.097 回答
1

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

当您拥有它时,也许您可​​以使用 Python API 异常调用之一来触发解释器中的异常?请参阅Python C API 异常

于 2009-09-14T11:40:22.153 回答