使用Python实现单个消费者,多生产者场景的最佳方式是什么?

时间:2015-06-10 21:43:04

标签: python multithreading synchronization producer-consumer

我有一个Python程序,它生成多个生成器线程,然后有一个循环,等待Queue对象中有一些东西。它看起来像这样

for t in threads:
    t.start()
while len(threads):
    if not queue.empty():
        response = queue.get()
        # handle response
        queue.task_done()
    else:
        sleep(1)
    threads = [t for t in threads if t.is_alive()]

必须有一种更优雅的方式来做到这一点。我查看了threading模块提供的所有同步对象,但我不知道如何应用它们。

仅供参考,我的代码是我正在尝试做的。我坚信不修复那些没有被破坏的东西,但我觉得有一种更好的方法可以让一个更好的程序员首先做到这一点。

2 个答案:

答案 0 :(得分:2)

如果线程仍然存在,您可以使用weakref来测试:

import weakref

def consumer(queue, threads):
    while threads:
        try:
            response = queue.get(timeout=1)
            # handle response
            queue.task_done()
        except Empty:
            pass

threads = weakref.WeakSet()
for i in range(10):
    t = threading.Thread(...)
    t.start()
    threads.add(t)
del t  # remove all references to threads

consumer(queue, threads)

答案 1 :(得分:2)

@ Daniel:weakref是一个很酷的技巧。这是一种替代方法,只使用添加了“终止策略”的队列。

您需要确保每个生产者的线程目标函数始终将最终的“终止消息”放入队列,在生成完成后基本上为“无”。消费者只是等待直到接收到适当数量的终端(每个生产者线程1个)并退出循环。这样您就不必检查线程是否已经结束,并且实际上只有一个通信点:队列。但是,如果消费者中存在异常,那么生产者线程可能应该处于“守护程序”模式,这样他们就不会在等待消费者队列的时候阻止该过程......好吧,消耗。

您必须确保始终为每个制作人发送终止消息,并采用某种try-finally缩进。否则,你将不得不处理消费者的除空之外的超时。

import functools
def consumer(queue,num_threads_remaining):
    next_message=functools.partial(iter,functools.partial(queue.get,timeout=1),None)
    while num_threads_remaining:
        try:
            for response in next_message():
                # handle response.. hopefully exception-protected
                queue.task_done()
            # we got a None termination message
            num_threads_remaining -= 1
        except Empty: pass # handle some other check when idling?