我有一个使用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
是否为空。
答案 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 documentation,empty()
,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)
如果每个线程都有可变的工作量(并且你事先不知道总数),这很有用,但可以确定它何时完成。