Python multiprocessing.Queue vs multiprocessing.manager()。Queue()

时间:2017-04-16 16:10:50

标签: python queue multiprocessing python-multiprocessing

我有一个简单的任务:

def worker(queue):
    while True:
        try:
            _ = queue.get_nowait()
        except Queue.Empty:
            break

if __name__ == '__main__':
    manager = multiprocessing.Manager()
    # queue = multiprocessing.Queue()
    queue = manager.Queue()

    for i in range(5):
        queue.put(i)

    processes = []

    for i in range(2):
        proc = multiprocessing.Process(target=worker, args=(queue,))
        processes.append(proc)
        proc.start()

    for proc in processes:
        proc.join()

似乎multiprocessing.Queue可以完成我需要的所有工作,但另一方面,我看到很多manager()。Queue()的例子,并且无法理解我真正需要的东西。看起来像Manager()。Queue()使用某种代理对象,但我不明白这些目的,因为multiprocessing.Queue()在没有任何代理对象的情况下完成相同的工作。

所以,我的问题是:

1)multiprocessing.manager()返回的multiprocessing.Queue和object之间究竟有什么区别.Queue()?

2)我需要使用什么?

2 个答案:

答案 0 :(得分:21)

虽然我对这个主题的理解是有限的,但从我所做的我可以看出,multiprocessing.Queue()和multiprocessing.Manager()之间存在一个主要区别.Queue():

  • multiprocessing.Queue()是一个对象,而multiprocessing.Manager()。Queue()是一个指向由multiprocessing.Manager()对象管理的共享队列的地址(代理)。
  • 因此,您无法将正常的multiprocessing.Queue()对象传递给Pool方法,因为它无法被pickle。
  • 此外python doc告诉我们在使用multiprocessing.Queue()时要特别注意,因为它会产生不良影响
  

注意当一个对象被放入一个队列时,该对象被腌制,一个后台线程随后将该pickle数据刷新到一个底层管道。这有一些后果有点令人惊讶,但不应该造成任何实际困难 - 如果他们真的打扰你,那么你可以改为使用由经理创建的队列。   将对象放在空队列之后,在队列的empty()方法返回False之前可能存在无穷小的延迟,并且get_nowait()可以返回而不会引发Queue.Empty。   如果多个进程将对象排入队列,则可能无序地在另一端接收对象。但是,由相同进程排队的对象将始终按预期的顺序相互关联。

     

警告如上所述,如果子进程已将项目放入队列(并且尚未使用JoinableQueue.cancel_join_thread),则该进程将不会终止,直到所有已缓冲的项目都被刷新为管道。   这意味着,如果您尝试加入该进程,则可能会遇到死锁,除非您确定已经使用了已放入队列的所有项目。类似地,如果子进程是非守护进程,则父进程在尝试加入其所有非守护进程子进程时可能会在退出时挂起。   请注意,使用管理器创建的队列没有此问题。

通过将队列设置为全局变量并在初始化时为所有进程设置它,可以使用带有池的多处理.Queue():

queue = multiprocessing.Queue()
def initialize_shared(q):
    global queue
    queue=q

pool= Pool(nb_process,initializer=initialize_shared, initargs(queue,))

将使用正确的共享队列创建池进程,但我们可以争辩说没有为此用途创建multiprocessing.Queue()对象。

另一方面,manager.Queue()可以通过将它作为函数的普通参数传递来在池子进程之间共享。

在我看来,使用multiprocessing.Manager()。Queue()在每种情况下都很好,而且不那么麻烦。使用经理可能存在一些缺点,但我并不知道。

答案 1 :(得分:1)

我最近遇到了Manager().Queue()的问题,当SyncManager返回的multiprocessing.Manager()对象似乎死了,并且它管理的队列永远阻塞(即使{{1 }}。

我不确定原因,或者SyncManager真的死了,我唯一的线索是我从具有*_nowait()的类实例中调用multiprocessing.Manager(),该实例记录了进程是从调用的,我可以看到这是从SyncManager进程调用的__del__()

这意味着我的对象在SyncManager进程中具有副本,并且已对其进行垃圾回收。这可能意味着只删除了我的对象,并且SyncManager很好,但是我确实看到相应的队列变得无响应,这与SyncManager进程中的__del__()调用相关。

我不知道我的对象如何在SyncManager进程中结束。我通常会抽出50-200名经理-有些人的生命周期重叠,有些则没有-直到我看到这个问题。对于在解释器退出时存在的对象,不会调用__del__(),而且我通常看不到SyncManager对象因__del__()上的此日志而死。可能是在出现问题时,SyncManager对象首先处理其对象,然后才退出解释器,这就是为什么我偶尔看到__del__()调用的原因。

即使在没有从SyncManager调用__del__()的情况下,我也确实看到队列没有响应。

我也看到了SyncManager的“死亡”,而没有引起更多问题。

“不响应”是指:

__del__()

永远不会回来。

queue.get(timeout=1)
queue.put(timeout=1)

永远不会回来。

这变得更加复杂,然后我本来想要,但是为了让别人有所帮助,我还是保留了细节。

我使用queue.get_nowait(timeout=1) queue.put_nowait(timeout=1) 很长时间了,没有任何问题。我怀疑实例化许多管理器对象会导致此问题,还是实例化许多管理器导致问题始终存在。

我使用Manager().Queue()