在MacOS上使用Python 3.7:
我有一个多处理池,子进程在其中处理工作单元。
反过来,这些Pool子进程中的每个子进程都必须运行命令子进程,这是使用Popen完成的;例如:
with Popen([CMD, ARGS], stdin=PIPE, stdout=PIPE, stderr=STDOUT, encoding='utf8', close_fds=True, cwd=JSBOT_DIR, start_new_session=True, ) as process:
try:
while process.poll() is None:
(ready, _, error) = select([process_stdout], [], [process_stdout], timeout)
if process_stdout in ready:
line = process_stdout.readline()
while line:
LOGGER.info(f'{line.rstrip()}')
line = process_stdout.readline()
except KeyboardInterrupt:
LOGGER.info('Got KeyboardInterrupt from subprocess')
# ...
process.wait(10)
可能会通过发送SIGUSR2信号通知该命令子进程结束包装并退出。
此命令子流程完成后,将进行一些后处理。这可以通过使用Pool子项中的subprocess.run
来实现,例如:
result = subprocess.run(['ffmpeg', '-y', '-i', pathname, '-ac', '1', '-ar', '48000', '-c:a', 'pcm_s16le',
normalized_filename], stderr=STDOUT, stdout=PIPE)
我遇到的问题是,在执行subprocess.run()
之后,Python立即发出了KeyboardInterrupt,并且 Pool 子项立即死亡:
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/subprocess.py", line 474, in run
stdout, stderr = process.communicate(input, timeout=timeout)
File "/usr/local/Cellar/python/3.7.3/Frameworks/Python.framework/Versions/3.7/lib/python3.7/subprocess.py", line 926, in communicate
stdout = self.stdout.read()
KeyboardInterrupt
我尝试设置SIGUSR2和SIGINT,以确保在子初始化期间在Pool子进程中将其忽略;但是,这没有帮助。我也尝试了有无start_new_session = True。我也尝试将SIGINT设置为在Pool子级中处理,但是在发生这种情况时不会调用处理程序。如果我故意向Pool子级发送SIGINT,则会调用处理程序。
如果在尝试创建下一个进程之前仅time.sleep(10)
进行该进程,它将休眠整个时间,然后立即认为KeyboardInterrupt在subprocess.run
内部进行第一次读取时就到达了。 / p>
出现的行为 使得向Pool子项的命令子进程传递信号导致Python认为已将KeyboardInterrupt发送给Pool子项,并且该KeyboardInterrupt在第一个IO上处理尝试。
我的问题是:为什么会发生这种情况(即,我错过了什么?),如何防止这种情况发生?
我还注意到,当Popened命令终止时,我在命令子进程上下文中捕获并记录了KeyboardInterrupt。这可能是解释器“记住”存在悬而未决的KeyboardInterrupt的情况,该中断最终在以后处理?如果是这样,我应该如何抑制它或“清除” KeyboardInterrupt?
编辑:随着我继续对此进行研究,我发现如果我注释掉所有的select / readline / log循环,则一切正常。因此,这似乎是从命令子进程中读取某些内容,从而杀死了Pool子级。