无法在嵌入 Python 的多线程 C 应用程序中终止多处理池



操作系统: linux
Python 版本: 3.6

我正在尝试使用 Python 运行时扩展 C 应用程序。C 应用程序使用pthread,我尝试在 Python 运行时中使用multiprocessingforkserver,但遇到了问题。当我尝试用SIGINT信号杀死程序时(通过在终端中按Ctrl+C(,工作进程被杀死,但主程序挂起。

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

#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("finishedn");
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 fsn"
"if __name__ == '__main__': fs.init()");
while(1){
pthread_t thread;
pthread_create(&thread, 0, &thread_start, NULL);
printf("joingn");
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。我想,目前,推荐的方法是在工作进程中捕获异常,而不是在主进程中捕获异常。

Py_Initialize()将安装python自己的singal handler,改为调用Py_InitializeEx(0)

void Py_InitializeEx(int initsigs(

此函数的工作方式类似于 Py_Initialize(( 如果 initsigs 为 1。如果 initsigs 为 0,则跳过 信号处理程序的初始化注册,这可能很有用 当 Python 被嵌入时。

查看更多关于其文档和 cPython 源代码的信息。

我更改了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);

事实证明,我不必在主进程中捕获异常。我通过在工作进程中捕获KeyboardInterrupt(或SIGINT(异常并将一些哨兵值发送到主进程以通知异常并关闭应用程序来解决问题。

import multiprocessing as mp

pool = None

def job():
try:
import time
print("running..")
time.sleep(5)
return True
except KeyboardInterrupt:
print("Exiting..")
return False
...
def apply(*args):
global pool
ret = pool.apply(*args)
if ret:
return pool.apply(*args)
else:
print("Gracefully die")

相关内容

  • 没有找到相关文章

最新更新