多处理pool.join()在某些情况下会挂起

时间:2016-02-23 23:10:29

标签: python multiprocessing producer-consumer pool

我正在尝试使用multiprocessing在Python中创建一个简单的生产者/消费者模式。它有效,但它挂在poll.join()上。

from multiprocessing import Pool, Queue

que = Queue()


def consume():
    while True:
        element = que.get()
        if element is None:
            print('break')
            break
    print('Consumer closing')


def produce(nr):
    que.put([nr] * 1000000)
    print('Producer {} closing'.format(nr))


def main():
    p = Pool(5)
    p.apply_async(consume)
    p.map(produce, range(5))
    que.put(None)
    print('None')
    p.close()
    p.join()


if __name__ == '__main__':
    main()

示例输出:

~/Python/Examples $ ./multip_prod_cons.py 
Producer 1 closing
Producer 3 closing
Producer 0 closing
Producer 2 closing
Producer 4 closing
None
break
Consumer closing

然而,当我更改一行时,它可以正常工作:

que.put([nr] * 100)

在运行Python 3.4.3或Python 2.7.10的Linux系统上100%可重现。我错过了什么吗?

1 个答案:

答案 0 :(得分:1)

这里有很多混乱。你所写的不是生产者/消费者的情景,而是混乱,滥用另一种通常被称为“工人池”的模式。

工人模式模式是生产者/消费者模式的应用程序,其中有一个生产者调度工作,许多消费者使用它。在这种模式中,Pool的所有者最终成为生产者,而工人将成为消费者。

在您的示例中,您可以使用混合解决方案,其中一个工作人员最终成为消费者,而其他人则充当中间件。整个设计效率非常低,重复了Pool已经提供的大部分逻辑,更重要的是,它非常容易出错。你最终遭受的是Deadlock

将对象放入multiprocessing.Queue是一种异步操作。仅当Queue已满且您的Queue具有无限大小时,它才会屏蔽。

这意味着您的produce函数会立即返回,因此对p.map的调用不会像您期望的那样阻塞。相关工作程序处理,等到实际消息通过Pipe用作通信通道的Queue

接下来发生的事情是,当您放入Queue None“消息”时,您会过早终止您的消费者,该消息会在您produce函数创建的所有列表被正确推送之前传递通过Pipe

您致电p.join后会发现问题,但实际情况如下。

  • p.join调用正在等待所有工作进程终止。
  • 工作进程正在等待大型列表通过Queue的{​​{1}}。
  • 由于消费者工作人员早已离开,没有人会消耗显然已满的Pipe

如果您的列表在您将终止消息实际发送到Pipe函数之前足够小,则问题不会显示。