在一个简单的脚本中,该脚本使用子进程gzip输出(使用subprocess.PIPE到外部命令的stdin),如果在创建子进程和关闭进程的stdin之间创建了multiprocessing.Pool对象,则subprocess.wait ()将永远挂起。
import multiprocessing
import subprocess
proc = subprocess.Popen(["gzip", "-c", "-"],
stdout=open('filename', 'w'), stdin=subprocess.PIPE)
multiprocessing.Pool()
proc.stdin.close()
proc.wait()
移动多重处理池向上或向下一行调用可避免此问题。
我在Python 2.7.3(Linux)和Python 2.7.1(OS X)上遇到了这个问题。
显然,这是一个简单的例子-实际用法要复杂得多。我也已经知道GzipFile了-我宁愿不使用它;通过使用子进程,我可以通过将gzip拆分为一个单独的线程来获得更多的CPU使用率。
我看不到如何简单地实例化Pool应该会产生这种影响。
调用时multiprocessing.Pool
,multiprocessing
模块将创建几个新进程(使用os.fork
或类似的进程)。
默认情况下,在期间fork
,新进程将继承所有打开的文件描述符。
当您subprocess.Popen
使用subprocess.PIPE
参数调用时,subprocess
模块将创建一些新的管道文件描述符,以向/从新进程发送数据。在这种特殊情况下,管道用于将数据从父进程(python)发送到子进程(gzip),并且proc.wait()
当对管道的所有写访问都消失时,gzip将退出并完成操作。(这就是在管道上生成“ EOF”的原因:该管道不再存在可写文件描述符。)
因此,在这种情况下,如果您(全部在“原始” python进程中)按以下顺序执行此操作:
multiprocessing.Pool
流程然后,由于的行为fork
,每个Pool进程都有一个os.dup
write-to-gzip管道,因此gzip继续等待更多的数据,这些Pool进程可以(但从不发送)发送。一旦Pool进程关闭其管道描述符,gzip进程将退出。
用实际的(更复杂的)代码解决这个问题可能并不容易。理想情况下,您想multiprocessing.Pool
知道的是(魔术地以某种方式)应该保留哪些文件描述符,而不应该保留哪些文件描述符,但这并不像“仅在创建的子进程中关闭一堆描述符”那样简单:
output = open('somefile', 'a')
def somefunc(arg):
... do some computation, etc ...
output.write(result)
pool = multiprocessing.Pool()
pool.map(somefunc, iterable)
显然output.fileno()
,这里的工作进程必须共享。
你可以尝试使用Pool
的initializer
调用proc.stdin.close
(或os.close
FD的名单上),但你需要安排跟踪描述为闭合的。重组代码以避免“在错误的时间”创建池可能是最简单的。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句