Queue.empty 在 Python 中的奇怪行为

Weird behaviour of Queue.empty in 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())

如果我取消对睡眠的注释,输出是正确的:False,20。这怎么可能?此代码 应该 运行 顺序,这意味着在 q.empty() 计算时,队列已经填满。

无论有无 sleep(),输出都不确定。您看到的部分按顺序运行,但是,在幕后,q.put(thing)thing交给multiprocessing工作线程来完成实际工作改变队列。 .put() returns 然后,无论工作线程是否已设法将 thing 放入队列。

这会让你“真正”燃烧!例如,考虑这个程序:

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

您不能依赖调用 multiprocessing.Queue.empty() 的结果。

.empty() 的文档指出:

Return True if the queue is empty, False otherwise. Because of multithreading/multiprocessing semantics, this is not reliable.

文档还指出,一个单独的线程处理排队对象,导致观察到的行为:

When an object is put on a queue, the object is pickled and a background thread later flushes the pickled data to an underlying pipe. This has some consequences which are a little surprising, but should not cause any practical difficulties – if they really bother you then you can instead use a queue created with a manager.

After putting an object on an empty queue there may be an infinitesimal delay before the queue’s empty() method returns False and get_nowait() can return without raising queue.Empty.

你只有一个进程,所以使用 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() 检查队列中是否有元素,您应该简单地尝试弹出队列并在没有任何元素时阻塞。