无法杀死嵌入Python的多线程C应用程序中的多处理池

时间:2018-08-23 05:37:57

标签: python c multithreading multiprocessing python-c-api

操作系统:linux
Python版本:3.6

我正在尝试使用Python运行时扩展C应用程序。 C应用程序使用pthread,而我尝试在Python运行时中使用multiprocessing forkserver,但遇到了问题。当我尝试使用SIGINT信号杀死程序时(通过在终端中单击 Ctrl + C )杀死工作进程,但主程序挂起。 / p>

这是一个产生相同问题的玩具程序。

#include <Python.h>
#include <pthread.h>

void * thread_start(void *unsed)
{
    PyObject *fs_mod = PyImport_AddModule("fs");
    PyObject *apply_fn = PyObject_GetAttrString(fs_mod, "apply");
    PyObject *job_fn = PyObject_GetAttrString(fs_mod, "job");
    PyObject *job_args = Py_BuildValue("()");
    PyObject_CallFunctionObjArgs(apply_fn, job_fn, job_args, NULL);
    printf("finished\n");
    return NULL;
}

int main(){
    Py_Initialize();
    PyRun_SimpleString(
        "import sys; sys.path.append('...');"
        "sys.argv=['a.out'];"  // prepare a dummy argument to avoid error in forkserver
        "import fs\n"
        "if __name__ == '__main__': fs.init()");

    while(1){
        pthread_t thread;
        pthread_create(&thread, 0, &thread_start, NULL);
        printf("joing\n");
        pthread_join(thread, 0);
    }
}
import multiprocessing as mp

pool = None


def job():
    import time
    print("running..")
    time.sleep(5)

def init():
    global pool
    mp.set_start_method('forkserver')
    pool = mp.Pool(1)

def apply(*args):
    global pool
    return pool.apply(*args)

我不确定Linux信号如何工作。我试图使用信号模块在主python进程中捕获SIGINT信号,但似乎主信号无法接收信号。我如何才能使该应用程序在SIGINT上正常死机而不会永远挂起?


通过阅读ViKiG答案,我意识到我可以首先在工作进程中捕获KeyboardInterrupt(或SIGINT)异常,并将一些哨兵值发送给主进程以通知该异常并关闭该应用程序。

浏览了CPython forkserver实现之后,我可能得出结论,该库的作者有意使主进程忽略了SIGINT。我猜,目前,推荐的方法是在工作进程中而不是在主要进程中捕获异常。

3 个答案:

答案 0 :(得分:1)

我将job函数更改为处理CTRL+C中断:

    def job():
        import time
        try:    
            while True:
                print("running..")
                time.sleep(5)
        except KeyboardInterrupt:
            print 'Exiting job..'

上述更改后,我的测试程序可以正常退出。

编辑后:

我将此添加到了C程序

    #include<signal.h>

    void handler() {printf("Exiting main.."); exit(0);}

main修改为:

    int main() {
        signal(SIGINT, handler);

答案 1 :(得分:1)

Py_Initialize()将安装python自己的单个处理程序,而是调用Py_InitializeEx(0)

  

void Py_InitializeEx(int initsigs)

     

此功能类似于   如果initsigs为1,则为Py_Initialize()。如果initsigs为0,则跳过   信号处理程序的初始化注册,这可能会有用   嵌入Python时。

详细了解其doccpython source

答案 2 :(得分:0)

事实证明,我不必在主要过程中捕获异常。我通过在工作进程中捕获short(或KeyboardInterrupt)异常并向主进程发送一些哨兵值来通知该异常并关闭应用程序,从而解决了该问题。

SIGINT