Python 优雅地处理子进程关闭
我在一个项目上工作,我有一批工人。我没有使用内置的Python 优雅地处理子进程关闭,python,multiprocessing,pipe,Python,Multiprocessing,Pipe,我在一个项目上工作,我有一批工人。我没有使用内置的多处理.Pool,而是创建了自己的进程池 它的工作方式是我创建了两个多处理.Queue实例——一个用于向工作人员发送工作任务,另一个用于接收结果 每个工人只是坐在一个永久运行的循环中,如下所示: while True: try: request = self.request_queue.get(True, 5) except Queue.Empty: continue else:
多处理.Pool
,而是创建了自己的进程池
它的工作方式是我创建了两个多处理.Queue
实例——一个用于向工作人员发送工作任务,另一个用于接收结果
每个工人只是坐在一个永久运行的循环中,如下所示:
while True:
try:
request = self.request_queue.get(True, 5)
except Queue.Empty:
continue
else:
result = request.callable(*request.args, **request.kwargs)
self.results_queue.put((request, result))
还有一些错误处理代码,但我把它留给了brewity。每个工作进程都将守护进程设置为1
我希望正确关闭主进程和所有子工作进程。我迄今为止的经验(使用Ctrl+C):
- 在没有特殊实现的情况下,每个子进程都会通过键盘中断回溯停止/崩溃,但主进程不存在,必须终止(
sudo kill-9
)
- 如果我为子进程实现一个信号处理程序,设置为忽略SIGINT,主线程将显示键盘中断跟踪框,但两种方式都不会发生
- 如果我为子进程和主进程实现一个信号处理程序,我可以看到信号处理程序在主进程中被调用,但是调用
sys.exit()
似乎没有任何效果
我正在寻找一种“最佳实践”的处理方法。我还读到,关闭与Queue
s和Pipe
s交互的进程可能会导致它们与其他进程死锁(由于内部使用了信号量和其他东西)
我目前的做法如下:
-找到一种方法向每个进程发送一个内部信号(使用一个单独的命令队列或类似命令队列),以终止它们的主循环。
-为发送关机命令的主回路实现一个信号处理程序。子进程将有一个子处理程序,将它们设置为忽略信号
这是正确的方法吗?您需要注意的是,在您想要关闭的时候,队列中可能存在消息,因此您需要一种方法让您的进程干净地排空其输入队列。假设您的主进程将认识到是时候关闭了,您可以这样做
向每个工作进程发送哨兵。这是一条特殊的消息(通常为None
),永远不会看起来像普通消息。在sentinel之后,刷新并关闭每个工作进程的队列
在工作进程中,使用与以下伪代码类似的代码:
while True: # Your main processing loop
msg = inqueue.dequeue() # A blocking wait
if msg is None:
break
do_something()
outqueue.flush()
outqueue.close()
如果可能有多个进程在inqueue
上发送消息,则需要更复杂的方法。此示例取自Python 3.2或更高版本中的logging.handlers.QueueListener
中的monitor
方法的源代码,显示了一种可能性
"""
Monitor the queue for records, and ask the handler
to deal with them.
This method runs on a separate, internal thread.
The thread will terminate if it sees a sentinel object in the queue.
"""
q = self.queue
has_task_done = hasattr(q, 'task_done')
# self._stop is a multiprocessing.Event object that has been set by the
# main process as part of the shutdown processing, before sending
# the sentinel
while not self._stop.isSet():
try:
record = self.dequeue(True)
if record is self._sentinel:
break
self.handle(record)
if has_task_done:
q.task_done()
except queue.Empty:
pass
# There might still be records in the queue.
while True:
try:
record = self.dequeue(False)
if record is self._sentinel:
break
self.handle(record)
if has_task_done:
q.task_done()
except queue.Empty:
break
您需要注意的是,在您希望关闭时,队列中可能存在消息,因此您需要一种方法让您的进程干净地排空其输入队列。假设您的主进程将认识到是时候关闭了,您可以这样做
向每个工作进程发送哨兵。这是一条特殊的消息(通常为None
),永远不会看起来像普通消息。在sentinel之后,刷新并关闭每个工作进程的队列
在工作进程中,使用与以下伪代码类似的代码:
while True: # Your main processing loop
msg = inqueue.dequeue() # A blocking wait
if msg is None:
break
do_something()
outqueue.flush()
outqueue.close()
如果可能有多个进程在inqueue
上发送消息,则需要更复杂的方法。此示例取自Python 3.2或更高版本中的logging.handlers.QueueListener
中的monitor
方法的源代码,显示了一种可能性
"""
Monitor the queue for records, and ask the handler
to deal with them.
This method runs on a separate, internal thread.
The thread will terminate if it sees a sentinel object in the queue.
"""
q = self.queue
has_task_done = hasattr(q, 'task_done')
# self._stop is a multiprocessing.Event object that has been set by the
# main process as part of the shutdown processing, before sending
# the sentinel
while not self._stop.isSet():
try:
record = self.dequeue(True)
if record is self._sentinel:
break
self.handle(record)
if has_task_done:
q.task_done()
except queue.Empty:
pass
# There might still be records in the queue.
while True:
try:
record = self.dequeue(False)
if record is self._sentinel:
break
self.handle(record)
if has_task_done:
q.task_done()
except queue.Empty:
break
我发现有一个基于进程的threading.Event克隆,它使向每个进程发送“stop”信号变得容易得多,我设置了一些类似于您编写的东西。我发现有一个基于进程的threading.Event克隆,它使向每个进程发送“stop”信号变得容易得多,我建立了一些类似于你所写的东西。