如何使Python的多处理队列的.empty()方法返回正确的值?还是其他选择?

时间:2015-09-04 09:56:22

标签: python multithreading python-2.7 python-multiprocessing

我有一个使用Queue模块中的multiprocess类的代码段。我很困惑,.empty()实例的Queue方法并没有像我期望的那样给出正确的值。这是我的代码:

from time import sleep
from multiprocessing import Queue, Lock

foo = Queue()
locker = Lock()

with locker:  # even with this, still True
    foo.put("bar")

print(foo.empty())  # True, obviously not
print(foo.empty())  # True
print(foo.empty())  # True
print(foo.qsize())  # 1L
print(foo.empty())  # True

但是,如果我使用sleep中的time函数,则会导致执行时间延迟。它有效。

from time import sleep
from multiprocessing import Queue, Lock

foo = Queue()
locker = Lock()

foo.put("bar")

sleep(0.01)

print(foo.empty())  # False
print(foo.empty())  # False
print(foo.empty())  # False
print(foo.qsize())  # 1L
print(foo.empty())  # False

我知道我的替代方案是.qsize() > 0表达式,但我确信我只是以错误的方式执行此操作。

我做错了什么?

*编辑*

我现在明白,不可靠,谢谢@Mathias Ettinger。任何干净的选择?我需要知道热才能可靠地判断我的Queue是否为空。

2 个答案:

答案 0 :(得分:7)

不幸的是,Queue的复杂实现意味着.empty()不能保证立即正确,即使.qsize()是。

由于您的平台支持.qsize() not true everywhere),因此您可以.empty()重新执行.qsize()检查},这对你有用:

# mp.Queue() is a function, not a class, so we need to find the true class
# to subclass
import multiprocessing.queues

class XQueue(multiprocessing.queues.Queue):
    def empty(self):
        try:
            return self.qsize() == 0
        except NotImplementedError:  # OS X -- see qsize() implementation
            return super(XQueue, self).empty()

在底层,队列.put()将您的对象添加到缓冲区并增加进程间信号量,而隐藏的守护程序线程负责耗尽缓冲区并将其内容序列化为管道。 (然后消费者通过读取此管道来.get()。)因此,这就是为什么在您的示例中工作的原因:守护程序线程有足够的时间将对象从内存缓冲区移动到I / O表示形式,然后再调用{{ 1}}。

顺便说一句,我发现这种行为非常令人惊讶。通常,当我们说队列的大小方法(空/满/计数)是“不可靠”时,我们的意思是它们准确且一致,但可能立即陈旧,因为另一个生产者或者消费者可能已经改变了队列。 (例如,大多数单进程多线程队列,例如python的.empty(),“不可靠”但在这种意义上是一致的。)在这种情况下,默认的Queue.Queue方法确实可能与状态不一致队列。

答案 1 :(得分:6)

根据the documentationempty()full()qsize()都不可靠。

替代方案包括:

  • 阅读Queue的确切项目数量:

    AMT = 8
    for _ in range(AMT):
        queue.put('some stuff')
    
    for _ in range(AMT):
        print(queue.get())
    

    如果您事先知道必须处理多少项目或每个线程将处理多少项目,这将非常有用。

  • 阅读项目,直到监护人出现:

    num_threads = 8
    guardian = 'STUFF DONE'
    
    while num_threads:
        item = queue.get()
        if item == guardian:
            num_threads -= 1
        else:
            process(item)
    

    如果每个线程都有可变的工作量(并且你事先不知道总数),这很有用,但可以确定它何时完成。