繁体   English   中英

Python multiprocessing.Queue 与 multiprocessing.manager().Queue()

[英]Python multiprocessing.Queue vs multiprocessing.manager().Queue()

我有一个简单的任务:

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.Queue 和 multiprocessing.manager().Queue() 返回的对象有什么区别?

2)我需要使用什么?

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

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

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

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

通过将队列设置为全局变量并在初始化时为所有进程设置它,有一种将 multiprocessing.Queue() 与 Pool 结合使用的解决方法:

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() 并且不那么麻烦。 使用经理可能有一些缺点,但我不知道。

我最近遇到了Manager().Queue() ,当SyncManager对象 - 由multiprocessing.Manager()返回 - 似乎死了,并且它管理的队列永远阻塞(即使使用*_nowait() )。

我不确定原因,或者如果 SyncManager 真的死了,我唯一的线索是我从一个类实例调用multiprocessing.Manager() ,它有__del__() ,它记录了它被调用的过程,并且我可以看到这是从 SyncManager 进程调用的__del__()

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

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

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

我还看到 SyncManager “死亡”而没有引起进一步的问题。

“无响应”是指:

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

永远不会回来。

queue.get_nowait(timeout=1)
queue.put_nowait(timeout=1)

永远不会回来。

这变得有点复杂,然后我最初想要,但我让细节进来,以防万一它对某人有帮助。

我之前使用Manager().Queue()很长时间没有任何问题。 我怀疑要么是实例化了很多管理器对象导致了这个问题,要么是实例化了很多管理器导致了一个一直存在于表面的问题。

我使用Python 3.6.5

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM