Python 如何处理异常子进程终止?
我正在使用Python3.7并遵循这一点。我想要一个进程,它应该产生一个子进程,等待它完成一项任务,然后返回一些信息。我使用以下代码: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() 当子进程正确地完成时,就没有问题了,它工作得很好,但是当我的子进程在完成之前终止时,问题就开始了
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。为了简单起见,我们假设它通过任务管理器被用户杀死。