Python多处理:如何限制等待进程的数量?

时间:2012-06-15 18:21:20

标签: python multiprocessing pool

使用Pool.apply_async运行大量任务(具有大参数)时,将分配进程并进入等待状态,并且对等待进程的数量没有限制。这可能最终会占用所有内存,如下例所示:

import multiprocessing
import numpy as np

def f(a,b):
    return np.linalg.solve(a,b)

def test():

    p = multiprocessing.Pool()
    for _ in range(1000):
        p.apply_async(f, (np.random.rand(1000,1000),np.random.rand(1000)))
    p.close()
    p.join()

if __name__ == '__main__':
    test()

我正在寻找一种限制等待队列的方法,使得只有有限数量的等待进程,并且在等待队列已满时阻塞了Pool.apply_async。

4 个答案:

答案 0 :(得分:6)

multiprocessing.Pool的{​​{1}}成员类型为_taskqueue,其中包含可选的multiprocessing.Queue参数;不幸的是,它没有maxsize参数集构建它。

我建议将maxsize子类化为multiprocessing.Pool的复制粘贴,将multiprocessing.Pool.__init__传递给maxsize构造函数。

对对象(池或队列)进行猴子修补也会有效,但你必须使用monkeypatch _taskqueuepool._taskqueue._maxsize这样才会很脆弱:

pool._taskqueue._sem

答案 1 :(得分:1)

等待pool._taskqueue超过所需的大小:

import multiprocessing
import numpy as np
import time

def f(a,b):
    return np.linalg.solve(a,b)

def test(max_apply_size=100):
    p = multiprocessing.Pool()
    for _ in range(1000):
        p.apply_async(f, (np.random.rand(1000,1000),np.random.rand(1000)))

        while pool._taskqueue.qsize() > max_apply_size:
            time.sleep(1)

    p.close()
    p.join()

if __name__ == '__main__':
    test()

答案 2 :(得分:1)

这是最佳答案的猴子修补替代方案:

import queue
from multiprocessing.pool import ThreadPool as Pool


class PatchedQueue():
  """
  Wrap stdlib queue and return a Queue(maxsize=...)
  when queue.SimpleQueue is accessed
  """

  def __init__(self, simple_queue_max_size=5000):
    self.simple_max = simple_queue_max_size  

  def __getattr__(self, attr):
    if attr == "SimpleQueue":
      return lambda: queue.Queue(maxsize=self.simple_max)
    return getattr(queue, attr)


class BoundedPool(Pool):
  # Override queue in this scope to use the patcher above
  queue = PatchedQueue()

pool = BoundedPool()
pool.apply_async(print, ("something",))

这在 Python 3.8 中按预期工作,其中多处理池使用 queue.SimpleQueue 来设置任务队列。听起来 multiprocessing.Pool 的实现可能自 2.7 以来发生了变化

答案 3 :(得分:0)

您可以使用maxsize参数添加显式队列,在这种情况下使用queue.put()代替pool.apply_async()。然后工人流程可以:

for a, b in iter(queue.get, sentinel):
    # process it

如果要将内存中创建的输入参数/结果的数量限制为大约活动工作进程的数量,则可以使用pool.imap*()方法:

#!/usr/bin/env python
import multiprocessing
import numpy as np

def f(a_b):
    return np.linalg.solve(*a_b)

def main():
    args = ((np.random.rand(1000,1000), np.random.rand(1000))
            for _ in range(1000))
    p = multiprocessing.Pool()
    for result in p.imap_unordered(f, args, chunksize=1):
        pass
    p.close()
    p.join()

if __name__ == '__main__':
    main()