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()
检查队列中是否有元素,您应该简单地尝试弹出队列并在没有任何元素时阻塞。
我在 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 andget_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()
检查队列中是否有元素,您应该简单地尝试弹出队列并在没有任何元素时阻塞。