Python多处理池从第一个块的输入中吞下异常

时间:2017-11-10 23:29:33

标签: python multiprocessing

我正在编写一个读取大量文件的脚本,然后并行处理所有这些文件中的行。

我的问题是,如果脚本无法打开某些文件,则表现得很奇怪。如果它是列表中的后一个文件之一,则它处理早期文件,并在到达坏文件时报告异常。但是,如果它无法打开列表中的第一个文件之一,则它不会处理任何内容,也不会报告异常。

如何让脚本报告所有异常,无论它们在列表中的哪个位置?

关键问题似乎是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

您可以看到异常被正确报告,直到块大小增加到足以在提交第一个块之前发生异常。然后它默默地失败,并且没有返回任何结果。

1 个答案:

答案 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版本。