【问题标题】:Stop embedded python停止嵌入的python
【发布时间】:2014-08-10 12:44:01
【问题描述】:

我正在将 python 嵌入到 C++ 插件中。插件在每个会话期间调用一个python算法数十次,每次发送算法不同的数据。到目前为止一切顺利

但现在我有一个问题: 该算法有时需要几分钟来解决并返回解决方案,并且在此期间,条件通常会发生变化,从而使该解决方案变得无关紧要。所以,我想要的是随时停止算法的运行,并在使用其他数据集后立即运行它。

这是我目前拥有的用于嵌入 python 的 C++ 代码:

void py_embed (void*data){


counter_thread=false;

PyObject *pName, *pModule, *pDict, *pFunc;

//To inform the interpreter about paths to Python run-time libraries
Py_SetProgramName(arg->argv[0]);

if(!gil_init){
    gil_init=1;
    PyEval_InitThreads();
    PyEval_SaveThread();
}
PyGILState_STATE gstate = PyGILState_Ensure();

// Build the name object
pName = PyString_FromString(arg->argv[1]);
if( !pName ){
    textfile3<<"Can't build the object "<<endl;
}

// Load the module object
pModule = PyImport_Import(pName);
if( !pModule ){
    textfile3<<"Can't import the module "<<endl;
}

// pDict is a borrowed reference 
pDict = PyModule_GetDict(pModule);
if( !pDict ){
    textfile3<<"Can't get the dict"<<endl;
}

// pFunc is also a borrowed reference 
pFunc = PyDict_GetItemString(pDict, arg->argv[2]);
if( !pFunc || !PyCallable_Check(pFunc) ){
    textfile3<<"Can't get the function"<<endl;
}

/*Call the algorithm and treat the data that is returned from it
 ...
 ...
 */

// Clean up
Py_XDECREF(pArgs2);
Py_XDECREF(pValue2);
Py_DECREF(pModule);
Py_DECREF(pName);

PyGILState_Release(gstate);

counter_thread=true;
_endthread(); 

};

编辑:python 的算法不是我的工作,我不应该改变它

【问题讨论】:

  • 算法可以分解成小步骤吗(最好是在有限时间内运行?)您的 C++ 代码可能是:while(stillNeeded) performNextStep();
  • 不,算法不是我的工作,我不应该改变它

标签: python c++ embed


【解决方案1】:

我一直在考虑这个问题,我同意子解释器可以为您提供一种可能的解决方案https://docs.python.org/2/c-api/init.html#sub-interpreter-support。它支持创建新解释器和结束现有解释器的调用。错误和警告部分描述了一些问题,根据您的架构可能会或可能不会出现问题。

另一种可能的解决方案是使用 python multiprocessing 模块,并在您的工作线程中测试一个全局变量(类似于 time_to_die)。然后从父级获取 GIL,设置变量,释放 GIL 并等待子级完成。

但后来我产生了另一个想法。为什么不直接使用 fork(),在子进程中初始化你的 python 解释器,当父进程决定是时候结束 python 线程时,直接杀死它。像这样的:

void process() {

    int pid = fork();
    if (pid) {
        // in parent
        sleep(60);
        kill(pid, 9);   
        }
    else{
        // in child
        Py_Initialize();
        PyRun_SimpleString("# insert long running python calculation");
        }
    }

(此示例假设 *nix,如果您在 Windows 上,请替换 CreateProcess()/TerminateProcess())

【讨论】:

  • 建议 1 不好,因为我认为任何新的子解释器都必须再次导入模块,这需要一些时间。至于其他 2 条建议,我更喜欢 fork() (即使我还不知道它是什么)但我肯定会研究两者。迄今为止的最佳答案,为此我奖励赏金
  • 只有一件事,我在 Windows 下,不容易理解这个 CreateProcess() 东西。你能把你的例子从linux更新到windows吗?
  • CreateProcess 是用于派生子进程的 win32 调用,与父进程断开连接。这是一个简单的例子msdn.microsoft.com/en-us/library/windows/desktop/…。这个概念与 fork() 相同,尽管 Microsoft 调用有很多额外的选项(其中大部分可以只是 NULL)。你的新孩子的第一个动作是初始化 python 并“做这件事”。然后,您可以随时杀死它——完全安全。当你选择杀死它时,它不会对父母产生影响。
【解决方案2】:

所以,我终于想到了一个解决方案(实际上更像是一种解决方法)。

我没有终止运行算法的线程(我们称之为 T1),而是使用当时相关的数据集创建另一个线程 -T2。

在每个线程中我都这样做:

thread_counter+=1; //global variable
int thisthread=thread_counter;

在给出 python 的解决方案后,我只是验证哪个是最新的,是来自 T1 还是来自 T2:

if(thisthread==thread_counter){
     /*save the solution and treat it */
}

就计算机工作而言,这显然不是最好的解决方案,但它符合我的目的。

感谢大家的帮助

【讨论】:

  • 你真的确定这个解决方案吗?您最终可能会遇到线程越来越多的情况,并且由于每个线程的 cpu 时间减少,您可能需要等待更长的时间。
  • 实际上,在尝试了一段时间后,我发现问题正是您所说的......是时候尝试一些建议的解决方案了
【解决方案3】:

这是基于对 python 的粗略了解,并快速阅读 python 文档。

PyThreadState_SetAsyncExc 允许您将异常注入到正在运行的 python 线程中。

在某个线程中运行您的 python 解释器。在另一个线程中,PyGILState_STATE 然后PyThreadState_SetAsyncExc 进入主线程。 (这可能需要一些前期工作来教 python 解释器关于第二个线程)。

除非您正在运行的 python 代码充满了“catch alls”,否则这应该会导致它终止执行。

您还可以查看代码以创建 python 子解释器,这样您就可以在旧脚本关闭时启动新脚本。

Py_AddPendingCall 也很诱人,但它周围有足够的警告可能没有。

【讨论】:

【解决方案4】:

抱歉,您的选择很短。您可以更改 python 代码(好的,插件 - 不是一个选项)或在另一个 PROCESS 上运行它(之间有一些不错的 ipc)。然后就可以使用系统api来清除了。

【讨论】:

  • 所以,如果我理解正确,您的建议是使用进程而不是线程?或者更好的是,将py_embedthread 放入一个进程并在我想要的时候杀死它?我很惊讶 Python/C API 中没有选项可以终止主线程或其他线程中的工作线程......
  • @JoãoPereira 在线程访问某些未知内存部分时终止线程确实会阻止它运行,但它会使您的应用程序处于难以预测的状态。您可能会泄漏文件句柄或内存,破坏堆(假设您正在将内存返回到堆的中间,并且在某些指令上您只需停止......),或其他类似的操作。操作系统在终止(一个进程)时提供了一个具有已知属性的胶囊,以及对这种包装的所有附加性能损失。
  • @JoãoPereira,(控制)应该通过在线程内运行的 python 代码的协议(和参与——例如注册一个信号)来完成——但如果你不能掌握 python另一方面,你必须使用技巧来处理它。
猜你喜欢
  • 2010-11-28
  • 1970-01-01
  • 1970-01-01
  • 2013-02-16
  • 2012-05-01
  • 1970-01-01
  • 2018-06-28
  • 2015-08-16
  • 1970-01-01
相关资源
最近更新 更多