我在Python中遇到了多处理的Queue.empty((的这个奇怪问题。在用元素填充之后,下面的代码输出为True和20。
from multiprocessing import Queue
import random
q = Queue()
for _ in range(20):
q.put(random.randint(0, 2))
#time.sleep(0.01)
print(q.empty())
print(q.qsize())
如果我取消对sleep的注释,那么输出是正确的:False,20。这怎么可能?此代码应该按顺序运行,这意味着当q.empty((求值时,队列已经填充。
您不能依赖于对multiprocessing.Queue.empty()
的调用的结果
.empty()
的文档说明:
如果队列为空,则返回
True
,否则返回False
由于存在多线程/多处理语义,这是不可靠的
文档还指出,一个单独的线程处理排队对象,导致观察到的行为:
当一个对象被放在队列中时,该对象会被pickle,后台线程稍后会将pickle的数据刷新到底层管道。这会产生一些有点令人惊讶的后果,但不应该造成任何实际困难——如果它们真的困扰你,那么你可以使用由管理器创建的队列。
将对象放入空队列后,在队列的
empty()
方法返回False之前可能会有一个无穷小的延迟并且get_nowait()
可以在不引发队列的情况下返回。空的
您只有一个进程,因此使用Queue
模块中的队列,该模块不依赖于另一个线程将数据添加到队列中:
from queue import Queue
import random
q = Queue()
for _ in range(20):
q.put(random.randint(0, 2))
print(q.empty())
print(q.qsize())
如果必须使用多个进程,则应尽量减少对.empty()
的依赖,因为其结果不可靠。例如,您不应该使用.empty()
来检查队列中是否有元素,而应该简单地尝试弹出队列并在没有任何元素的情况下进行阻止。
无论是否使用sleep()
,输出都是不确定的。看到的部分按顺序运行,但是,在隐蔽的情况下,q.put(thing)
将thing
交给multiprocessing
工作线程,以完成更改队列的实际工作。不管工作线程是否已成功将thing
放入队列,.put()
都会立即返回。
这可能会灼伤你"真的"!例如,考虑这个程序:
import multiprocessing as mp
import time
q = mp.Queue()
nums = list(range(20))
q.put(nums)
# time.sleep(2)
del nums[-15:]
print(q.get())
很可能会显示:
[0, 1, 2, 3, 4]
即使某些其他进程从q
检索,情况也是如此。q.put(nums)
放弃了对nums
进行酸洗并将其序列化形式放入队列的任务,这与主程序更改nums
之间存在竞争。
如果取消对sleep(2)
的注释,那么它很可能会显示原始的20元素nums
。