获取OSError:在使用多处理的代码上运行冗长的测试套件时,打开的文件太多

时间:2016-12-23 01:08:22

标签: python file-io multiprocessing python-multiprocessing contextmanager

运行python 2.7,dev环境是OS X但生产是linux。

我已经获得了一些代码,我试图加快多处理速度,并使其工作正常,并观察到理想的理论加速。然后我去运行测试套件,经过几次测试后,开始在所有后续测试中获得上述OSError。如果我从我开始收到错误的位置运行测试,其中一些数字通过,然后我再次得到该错误。这是合乎逻辑的,只是一个健全性检查。

为了弄清楚出现了什么问题,我将__builtin__的{​​{1}}和open来电替换为打印出来的(按照https://stackoverflow.com/a/2023709/3543200中的建议)

close

我看到的是几百行import __builtin__ import traceback import sys openfiles = set() oldfile = __builtin__.file class newfile(oldfile): def __init__(self, *args): self.x = args[0] print "### OPENING %s ###" % str(self.x) traceback.print_stack(limit=20) print sys.stdout.flush() oldfile.__init__(self, *args) openfiles.add(self) def close(self): print "### CLOSING %s ###" % str(self.x) oldfile.close(self) openfiles.remove(self) oldopen = __builtin__.open def newopen(*args): return newfile(*args) __builtin__.file = newfile __builtin__.open = newopen

当我为完成相同任务但没有多处理的代码执行相同的操作时,我没有得到这样的文件连接,因此可以理解多处理在这里是错误的。 ### OPENING /dev/null ###调用支持这一点,这表明罪魁祸首在于:

traceback

在此处发布 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/multiprocessing/process.py", line 250, in _bootstrap sys.stdin = open(os.devnull) 功能的代码,以防它有用:

multiprocessing::process.py::_bootstrap

而且,为了它的价值,我使用如下代码调用多处理:

def _bootstrap(self):
    from . import util
    global _current_process

    try:
        self._children = set()
        self._counter = itertools.count(1)
        try:
            sys.stdin.close()
            sys.stdin = open(os.devnull)
        except (OSError, ValueError):
            pass
        _current_process = self
        util._finalizer_registry.clear()
        util._run_after_forkers()
        util.info('child process calling self.run()')
        try:
            self.run()
            exitcode = 0
        finally:
            util._exit_function()
    except SystemExit, e:
        if not e.args:
            exitcode = 1
        elif isinstance(e.args[0], int):
            exitcode = e.args[0]
        else:
            sys.stderr.write(str(e.args[0]) + '\n')
            sys.stderr.flush()
            exitcode = 1
    except:
        exitcode = 1
        import traceback
        sys.stderr.write('Process %s:\n' % self.name)
        sys.stderr.flush()
        traceback.print_exc()

    util.info('process exiting with exitcode %d' % exitcode)
    return exitcode

所以,问:这是num_cpus = multiprocessing.cpu_count() pool = multiprocessing.Pool(processes=num_cpus) num_per_job = len(input_data) / num_cpus + 1 chunks = [input_data[num_per_job*i:num_per_job*(i+1)] for i in range(num_cpus)] # TODO: ^^^ make this a list of generators data = pool.map(get_output_from_input, chunks) return itertools.chain.from_iterable(data) 中的一个错误,还是我做了一件非常错误的事情?我真的很乐意借助下周挖掘multiprocessing代码并弄清楚它是如何工作的借口,但我很难说服高层人士认为这是对我的时间的有效利用。感谢任何有经验帮助的人!

1 个答案:

答案 0 :(得分:3)

您需要关闭池以终止子进程并释放用于与它们通信的管道。使用contextlib.closing执行此操作,以便您不必担心跳过关闭的异常。 closing将在with块的末尾关闭池,包括退出时出现异常。所以,你永远不需要亲自打电话。

此外,Pool.map会对其请求进行分块,因此您无需亲自执行此操作。我删除了那段代码,但get_output_from_input签名可能不正确(每个输入项将调用一次,而输入项列表则不会调用一次),因此您可能需要进行一些修改。

import contextlib
num_cpus = multiprocessing.cpu_count()
with contextlib.closing(multiprocessing.Pool(processes=num_cpus)) as pool:
    data = pool.map(get_output_from_input, input_data)
    return itertools.chain.from_iterable(data)