Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/328.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何处理异常子进程终止?_Python_Process_Multiprocessing_Queue_Python Multiprocessing - Fatal编程技术网

Python 如何处理异常子进程终止?

Python 如何处理异常子进程终止?,python,process,multiprocessing,queue,python-multiprocessing,Python,Process,Multiprocessing,Queue,Python Multiprocessing,我正在使用Python3.7并遵循这一点。我想要一个进程,它应该产生一个子进程,等待它完成一项任务,然后返回一些信息。我使用以下代码: if __name__ == '__main__': q = Queue() p = Process(target=some_func, args=(q,)) p.start() print q.get() p.join() 当子进程正确地完成时,就没有问题了,它工作得很好,但是当我的子进程在完成之前终止时,问题就开始了

我正在使用Python3.7并遵循这一点。我想要一个进程,它应该产生一个子进程,等待它完成一项任务,然后返回一些信息。我使用以下代码:

if __name__ == '__main__':
    q = Queue()
    p = Process(target=some_func, args=(q,))
    p.start()
    print q.get()
    p.join()
当子进程正确地完成时,就没有问题了,它工作得很好,但是当我的子进程在完成之前终止时,问题就开始了。 在这种情况下,我的应用程序挂起等待

q.get()
p.join()
一个超时并不能完全解决问题,因为我想立即知道子进程已死亡,而不是等待超时

另一个问题是
q.get()
上的超时会产生一个异常,我希望避免这种情况


有人能给我推荐一种更优雅的方法来解决这些问题吗?

队列和信号

一种可能是注册一个信号处理程序并使用它传递一个哨兵值。 在Unix上,您可以在父级中处理
SIGCHLD
,但在您的情况下,这不是一个选项。根据报告:

在Windows上,只能使用SIGABRT、SIGFPE、SIGILL、SIGINT、SIGSEGV、SIGTERM或SIBREAK调用signal()

不确定通过任务管理器杀死它是否会转化为
SIGTERM
,但您可以尝试一下

要处理
SIGTERM
,您需要在子系统中注册信号处理程序

import os
import sys
import time
import signal
from functools import partial
from multiprocessing import Process, Queue

SENTINEL = None


def _sigterm_handler(signum, frame, queue):
    print("received SIGTERM")
    queue.put(SENTINEL)
    sys.exit()


def register_sigterm(queue):
    global _sigterm_handler
    _sigterm_handler = partial(_sigterm_handler, queue=queue)
    signal.signal(signal.SIGTERM, _sigterm_handler)


def some_func(q):
    register_sigterm(q)
    print(os.getpid())
    for i in range(30):
        time.sleep(1)
        q.put(f'msg_{i}')


if __name__ == '__main__':

    q = Queue()
    p = Process(target=some_func, args=(q,))
    p.start()
    for msg in iter(q.get, SENTINEL):
        print(msg)
    p.join()
示例输出:

12273
msg_0
msg_1
msg_2
msg_3
received SIGTERM

Process finished with exit code 0

即使这在任务管理器中有效,您的用例听起来也不能排除强制杀戮,所以我认为您最好使用一种不依赖信号的方法

如果进程
p.处于活动状态()
,则可以在循环中进行检查,在指定
超时的情况下调用
queue.get()
,并处理
空的异常:

import os
import time
from queue import Empty
from multiprocessing import Process, Queue

def some_func(q):
    print(os.getpid())
    for i in range(30):
        time.sleep(1)
        q.put(f'msg_{i}')


if __name__ == '__main__':

    q = Queue()
    p = Process(target=some_func, args=(q,))
    p.start()

    while p.is_alive():
        try:
            msg = q.get(timeout=0.1)
        except Empty:
            pass
        else:
            print(msg)

    p.join()
也可以避免出现异常,但我不建议这样做,因为您不会将等待时间花在“排队”上,因此会降低响应速度:

while p.is_alive():
    if not q.empty():
        msg = q.get_nowait()
        print(msg)
        time.sleep(0.1)

如果您打算为每个孩子使用一个连接,那么可以使用管道而不是队列。这比排队更有效 (安装在管道顶部),您可以使用(Python 3.3+)一次等待多个对象就绪

多处理。连接。等待(对象列表,超时=无)

等待对象列表中的对象准备就绪。返回对象列表中已准备就绪的对象的列表。如果timeout是一个float,那么调用最多会阻塞几秒钟。如果timeout为None,则它将阻塞无限期。负超时相当于零超时

对于Unix和Windows,如果对象是可读的连接对象,则该对象可以出现在对象列表中; 连接且可读的socket.socket对象;或 进程对象的sentinel属性。 当有数据可从连接或套接字对象中读取,或另一端已关闭时,连接或套接字对象已准备就绪

Unix:等待(对象列表,超时)几乎等同于选择。选择(对象列表,[],[]超时)。区别在于,如果select.select()被信号中断,它可能会引发错误号为EINTR的OSError,而wait()则不会

Windows:对象列表中的项必须是可等待的整数句柄(根据Win32函数WaitForMultipleObjects()文档使用的定义),也可以是具有返回套接字句柄或管道句柄的fileno()方法的对象。(请注意,管道句柄和套接字句柄不是可等待的句柄。)

您可以使用它同时等待进程的sentinel属性和管道的父端

import os
import time
from multiprocessing import Process, Pipe
from multiprocessing.connection import wait


def some_func(conn_write):
    print(os.getpid())
    for i in range(30):
        time.sleep(1)
        conn_write.send(f'msg_{i}')


if __name__ == '__main__':

    conn_read, conn_write = Pipe(duplex=False)
    p = Process(target=some_func, args=(conn_write,))
    p.start()

    while p.is_alive():
        wait([p.sentinel, conn_read])  # block-wait until something gets ready
        if conn_read.poll():  # check if something can be received
            print(conn_read.recv())
    p.join()

你使用哪种操作系统?谁杀死了你的进程?通过哪个信号?我从Windows开始,虽然我也应该使用Linux。为了简单起见,我们假设它通过任务管理器被用户杀死。