Queue.empty在Python中的怪异行为



我在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

最新更新