在阅读@Martijn Pieters 之后,我最初删除了这个答案,因为他更详细和更早地描述了“为什么这不起作用”。然后
我意识到,OP 示例中的用例不太适合
的规范冠冕堂皇的标题
“如何使用 multiprocessing.Queue.get 方法”。
那不是因为有
演示不涉及子进程,但是因为在实际应用程序中,几乎没有一个队列是预先填充的,并且只在之后读取,但是读取
并且写入发生在中间的等待时间之间。 Martijn 展示的扩展演示代码在通常情况下不起作用,因为当排队跟不上读取速度时,while 循环会过早中断。所以这里是重新加载的答案,它能够处理通常的交错提要和读取场景:
不要依赖 queue.empty 检查同步。
在将对象放入空队列后,队列的 empty() 方法返回 False 和 get_nowait() 可以在不引发 queue.Empty 的情况下返回之前可能会有一个无限小的延迟。
...
empty()
如果队列为空,则返回 True,否则返回 False。由于多线程/多处理语义,这是不可靠的。 docs
使用队列中的for msg in iter(queue.get, sentinel): 到.get(),通过传递一个标记值来跳出循环...iter(callable, sentinel)?
from multiprocessing import Queue
SENTINEL = None
if __name__ == '__main__':
queue = Queue()
for i in [*range(3), SENTINEL]:
queue.put(i)
for msg in iter(queue.get, SENTINEL):
print(msg)
...如果您需要非阻塞解决方案,请使用get_nowait() 并处理可能的queue.Empty 异常。
from multiprocessing import Queue
from queue import Empty
import time
SENTINEL = None
if __name__ == '__main__':
queue = Queue()
for i in [*range(3), SENTINEL]:
queue.put(i)
while True:
try:
msg = queue.get_nowait()
if msg == SENTINEL:
break
print(msg)
except Empty:
# do other stuff
time.sleep(0.1)
如果只有一个进程且该进程中只有一个线程正在读取队列,也可以将最后一个代码 sn-p 交换为:
while True:
if not queue.empty(): # this is not an atomic operation ...
msg = queue.get() # ... thread could be interrupted in between
if msg == SENTINEL:
break
print(msg)
else:
# do other stuff
time.sleep(0.1)
由于线程可以在检查if not queue.empty() 和queue.get() 之间删除GIL,因此这不适用于进程中的多线程队列读取。如果多个进程正在从队列中读取,这同样适用。
不过,对于单一生产者/单一消费者场景,使用 multiprocessing.Pipe 而不是 multiprocessing.Queue 就足够了,而且性能更高。