我正在使用concurrent.futures来实现多处理。我得到一个队列。全错误,这很奇怪,因为我只分配了10个工作。
A_list = [np.random.rand(2000, 2000) for i in range(10)]
with ProcessPoolExecutor() as pool:
pool.map(np.linalg.svd, A_list)
错误:
Exception in thread Thread-9:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/threading.py", line 921, in _bootstrap_inner
self.run()
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/threading.py", line 869, in run
self._target(*self._args, **self._kwargs)
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/concurrent/futures/process.py", line 251, in _queue_management_worker
shutdown_worker()
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/concurrent/futures/process.py", line 209, in shutdown_worker
call_queue.put_nowait(None)
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/multiprocessing/queues.py", line 131, in put_nowait
return self.put(obj, False)
File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/multiprocessing/queues.py", line 82, in put
raise Full
queue.Full
答案 0 :(得分:16)
简答
我认为管道尺寸限制是根本原因。除了将数据分解成更小的块并迭代处理它们之外,您无法做很多事情。这意味着您可能需要找到一种新算法,该算法一次可以处理2000x2000阵列的一小部分,以找到奇异值组合。
<强>详情
让我们马上直接得到一件事:你正在处理大量信息。仅仅因为你只使用了十件物品并不意味着它是微不足道的。这些项目中的每一个都是一个2000x2000阵列,其中包含4,000,000个浮点数,每个浮点数通常为64位,因此您需要查看每个阵列大约244MB,以及Numpy's ndarrays中标记的其他数据。
ProcessPoolExecutor通过启动一个单独的线程来管理工作进程。管理线程使用multiprocesing.Queue将作业传递给工作人员,称为_call_queue
。这些multiprocessing.Queue
实际上只是pipes周围的花式包装器,而你试图传递给工人的ndarrays可能太大而无法正确处理管道。
阅读Python Issue 8426表明,即使您可以查找操作系统的一些标称管道尺寸限制,也要确定管道的确切尺寸。有太多变量使它变得简单。即使从队列中拉出事物的顺序也会在底层管道中引发触发奇怪错误的竞争条件。
我怀疑你的一名工作人员正在从_call_queue
获取一个不完整或已损坏的对象,因为该队列的管道中充满了你的巨型物体。那个工人以不干净的方式死亡,工作队列管理器检测到这个失败,所以它放弃工作并告诉剩下的工人退出。但它通过将poison pills传递给_call_queue
来实现这一点,{em>仍然充满了你的巨型ndarrays 。这就是您获得完整队列异常的原因 - 您的数据填满了队列,然后管理线程尝试使用相同的队列将控制消息传递给其他工作人员。
我认为这是在程序中不同实体之间混合数据和控制流的潜在危险的典型示例。您的大数据不仅会阻止更多数据被工作人员接收,还会阻止管理员与工作人员的控制通信,因为他们使用相同的路径。
我无法重新创建您的失败,因此我无法确定所有这些都是正确的。但是,你可以使这个代码使用200x200阵列(~2.5MB)这一事实似乎支持这一理论。标称管道大小限制似乎以KB或几MB为单位进行测量,具体取决于操作系统和体系结构。这一数据量可以通过管道这一事实并不令人惊讶,尤其是当您认为如果消费者不断接收数据时,并非所有2.5MB都需要同时实际适应管道。它建议您可以通过管道连续获得的数据量的合理上限。
答案 1 :(得分:6)
我最近在调试python3.6程序时偶然发现了这个问题,该程序通过管道发送各种GB的数据。这就是我发现的(希望可以节省别人的时间!)。
像skrrgwasme所说,如果队列管理器在发送毒丸时无法获取信号量,则会raises队列满错误。 信号量的acquire call是非阻塞的,它会导致管理器发生故障(由于数据和控制流共享相同的 Queue ,因此无法发送“ control”命令)。请注意,上面的链接引用了python 3.6.0
现在我想知道为什么我的队列管理器会发送毒药。肯定还有其他故障! 显然发生了一些异常(在其他子进程中?在父级中?),队列管理器试图清理并关闭所有子进程。在这一点上,我有兴趣找到这个根本原因。
调试根本原因
我最初尝试在子流程中记录所有异常,但显然那里没有发生显式错误。 来自issue 3895:
请注意,当处理失败时,multiprocessing.Pool也会中断。
似乎py36中的多处理模块已损坏,因为它无法正确捕获和处理序列化错误。
不幸的是,由于时间限制,我没有亲自复制和验证问题,而是希望跳到动作要点和更好的编程习惯(不要通过管道发送所有数据:)。这里有一些想法:
动作要点
对程序进行重构,以尽可能减少通过管道发送的数据。
在阅读issue 3895之后,似乎出现了酸洗错误的问题。一种替代方法(也是良好的编程习惯)可以使用不同的方法来传输数据。例如,可以让子进程写入文件,然后将路径返回到父进程(这只是一个很小的字符串,可能是几个字节)。
等待将来的python版本。显然,此问题已在issue 3895的上下文中固定在python版本标签v3.7.0b3上。 Full 异常将是 shutdown_worker 中的handled。撰写本文时,Python的当前维护版本是3.6.5