Python动态多处理和信令问题
我有一个带有自定义信号处理的pythonPython动态多处理和信令问题,python,python-2.7,multiprocessing,signals,Python,Python 2.7,Multiprocessing,Signals,我有一个带有自定义信号处理的pythonmultiprocessing设置(即工作进程),它阻止工作进程干净地使用multiprocessing本身。(请参阅下面的扩展问题描述) 设置 生成所有辅助进程的master类如下所示(某些部分剥离为仅包含重要部分) 这里,它重新绑定自己的信号s,只打印主拆卸;实际上,接收到的信号沿着流程树传播,必须由工作人员自己处理。这是通过在工人繁殖后重新绑定信号来实现的 类中间层(对象): 定义初始值(自,nprocs=2): self.nprocs=nprocs
multiprocessing
设置(即工作进程),它阻止工作进程干净地使用multiprocessing
本身。(请参阅下面的扩展问题描述)
设置
生成所有辅助进程的master类如下所示(某些部分剥离为仅包含重要部分)
这里,它重新绑定自己的信号
s,只打印主拆卸
;实际上,接收到的信号沿着流程树传播,必须由工作人员自己处理。这是通过在工人繁殖后重新绑定信号来实现的
类中间层(对象):
定义初始值(自,nprocs=2):
self.nprocs=nprocs
self.procs=[]
def手柄_信号(自身、信号、帧):
log.info(‘主拆卸’)
对于self.procs中的p:
p、 加入
sys.exit()
def启动(自):
#启动所需数量的工人
对于范围内的(NPROC):
p=工人()
self.procs.append(p)
p、 开始()
#在工作人员繁殖并启动后为主控绑定信号
信号.signal(signal.SIGINT,self.handle_信号)
signal.signal(signal.SIGTERM,self.handle_信号)
#永远服务,只在发出信号时退出
对于self.procs中的p:
p、 加入
worker类基于多处理.Process
并实现自己的run()
-方法
在该方法中,它连接到分布式消息队列,并永久轮询队列中的项目。永久应为:直到工作人员收到SIGINT
或SIGTERM
。工人不应立即辞职;相反,它必须完成它所做的任何计算,然后将退出(一旦quit\u req
设置为True
)
类工作者(流程):
定义初始化(自):
self.quit_req=False
进程。初始化(自)
def手柄_信号(自身、信号、帧):
打印('stoppingworker(pid:{})'.format(self.pid))
self.quit_req=True
def运行(自):
#为工作进程设置信号
信号.signal(signal.SIGINT,self.handle_信号)
signal.signal(signal.SIGTERM,self.handle_信号)
q=连接到某个分布式消息队列()
#开始消费
打印('Starting worker(pid:{})'.format(self.pid))
而不是自我退出请求:
message=q.poll()
如果是len(信息):
尝试:
打印({}处理消息“{}”。格式(
self.pid,消息)
)
#外观模式:为外观选择正确的目标功能
#请求消息并执行它。
MessageRouter.route(消息)
例外情况除外,如e:
打印({}处理{}失败:{}。格式(
self.pid、message、e.message)
)
问题
到目前为止,基本设置(几乎)一切正常:
- 主进程生成所需数量的工作进程
- 每个工作进程都连接到消息队列
- 消息发布后,其中一个工作人员将收到该消息
- facade模式(使用名为MessageRouter的类)将接收到的消息路由到相应的函数并执行它
消息被消息路由器
门面指向)可能包含非常复杂的业务逻辑,因此可能需要多处理
例如,如果目标函数包含以下内容:
nproc=4
#生成一个池,因为我们这里有昂贵的计算
p=池(进程=nproc)
#收集异步应用调用“某些计算”的结果代理对象
rpx=[p.apply_async(一些昂贵的计算,())用于范围内的(nproc)]
#收集所有过程的结果
res=[rpx中r的rpx.get(timeout=.5)]
#打印所有结果
打印(res)
然后,由池产生的进程也将把它们对SIGINT
和SIGTERM
的信号处理重定向到工作者的handle\u signal
函数(因为信号传播到进程子树),本质上打印停止工作者(pid:…)
。我知道,之所以会发生这种情况,是因为我在生成工作进程自己的子进程之前已经为其重新绑定了信号
这就是我被卡住的地方:在生成子进程后,我无法设置worker的信号,因为我不知道它是否生成了一些(目标函数被屏蔽,可能由其他人编写),并且因为worker停留在轮询循环中(按设计)。同时,我不能期望使用多处理
将自己的信号处理程序重新绑定到(任何)默认值的目标函数的实现
目前,我觉得恢复worker中每个循环中的信号处理程序(在消息被路由到其目标函数之前)并在函数返回后重置它们是唯一的选择,但感觉是错误的
我错过什么了吗?你有什么建议吗?如果有人能给我一个如何解决我的设计缺陷的提示,我会非常高兴 没有一个明确的方法来解决您希望继续处理的问题。我经常发现自己必须在多处理环境中运行未知代码(表示为Python入口点函数,这可能会导致一些C语言的怪异)
这就是我处理这个问题的方法
主循环
通常主循环是非常简单的
try:
while 1:
task = get_next_task()
service.process(task)
except KeyboardInterrupt:
service.wait_for_pending_tasks()
logging.info("Sayonara!")
from multiprocessing import Process, set_start_method
import sys
from signal import signal, SIGINT
from time import sleep
class NormalWorker(Process):
def run(self):
while True:
print('%d %s work' % (self.pid, type(self).__name__))
sleep(1)
class SpawningWorker(Process):
def handle_signal(self, signum, frame):
print('%d %s handling signal %r' % (
self.pid, type(self).__name__, signum))
def run(self):
signal(SIGINT, self.handle_signal)
sub = NormalWorker()
sub.start()
print('%d joining %d' % (self.pid, sub.pid))
sub.join()
print('%d %s joined sub worker' % (self.pid, type(self).__name__))
def main():
set_start_method('forkserver')
processes = [SpawningWorker() for ii in range(5)]
for pp in processes:
pp.start()
def sig_handler(signum, frame):
print('main handling signal %d' % signum)
for pp in processes:
pp.join()
print('main out')
sys.exit()
signal(SIGINT, sig_handler)
while True:
sleep(1.0)
if __name__ == '__main__':
main()
import os
from signal import SIGINT, SIG_DFL
def patch_fork():
print('Patching fork')
os_fork = os.fork
def my_fork():
print('Fork fork fork')
cpid = os_fork()
if cpid == 0:
# child
signal(SIGINT, SIG_DFL)
return cpid
os.fork = my_fork
if os.getpid() != main_pid:
sys.exit(128 + signum)