我正在编写一个读取大量文件的脚本,然后并行处理所有这些文件中的行。
我的问题是,如果脚本无法打开某些文件,则表现得很奇怪。如果它是列表中的后一个文件之一,则它处理早期文件,并在到达坏文件时报告异常。但是,如果它无法打开列表中的第一个文件之一,则它不会处理任何内容,也不会报告异常。
如何让脚本报告所有异常,无论它们在列表中的哪个位置?
关键问题似乎是pool.imap()
的块大小。如果在提交第一个块之前发生异常,则会以静默方式失败。
这是一个重现问题的小脚本:
from multiprocessing.pool import Pool
def prepare():
for i in range(5):
yield i+1
raise RuntimeError('foo')
def process(x):
return x
def test(chunk_size):
pool = Pool(10)
n = raised = None
try:
for n in pool.imap(process, prepare(), chunksize=chunk_size):
pass
except RuntimeError as ex:
raised = ex
print(chunk_size, n, raised)
def main():
print('chunksize n raised')
for chunk_size in range(1, 10):
test(chunk_size)
if __name__ == '__main__':
main()
prepare()
函数生成五个整数,然后引发异常。该生成器传递给pool.imap()
,块大小从1到10.然后它打印出块大小,接收结果数和任何引发的异常。
chunksize n raised
1 5 foo
2 4 foo
3 3 foo
4 4 foo
5 5 foo
6 None None
7 None None
8 None None
9 None None
您可以看到异常被正确报告,直到块大小增加到足以在提交第一个块之前发生异常。然后它默默地失败,并且没有返回任何结果。
答案 0 :(得分:1)
如果我在我自己的便利系统上使用Python 2.7.13和3.5.4运行它(我稍微修改了py2k和py3k交叉兼容性),我得到:
$ python2 --version
Python 2.7.13
$ python2 mptest.py
chunksize n raised
1 5 foo
2 4 foo
3 3 foo
4 4 foo
5 5 foo
6 None None
7 None None
8 None None
9 None None
$ python3 --version
Python 3.5.4
$ python3 mptest.py
chunksize n raised
1 5 foo
2 4 foo
3 3 foo
4 4 foo
5 5 foo
6 None foo
7 None foo
8 None foo
9 None foo
我认为它对于块大小>失败(因此打印None
)这一事实; 5并不奇怪,因为没有池进程可以获得六个参数,因为通过调用mptest
生成的生成器只能被调用5次。
令人惊讶的是,Python2.7.9对于大小超过5的块大小的异常说None
,而对于例外,Python 3.5说foo
。
这是Issue #28699,已在commit 794623bdb2中修复。该修补程序显然已经向后移植到Python 3.5.4,但不是Python 2.7.9,也不是你自己的Python 3版本。