Python3 queue 模块
Python3 的
queue 模块提供了线程安全的队列数据结构,用于在多线程环境中实现线程间的通信和数据共享。queue 模块实现了三种类型的队列,它们的主要区别在于元素的获取顺序。
模块概览
queue 模块定义了以下三个类:-
queue.Queue(maxsize=0):- FIFO (先进先出) 队列。
- 元素按照添加顺序排列,获取时从队列头部取出。
maxsize参数指定队列的最大容量。如果maxsize小于或等于 0,队列容量是无限的。当队列满时,put()操作会阻塞,直到有空间可用。
-
queue.LifoQueue(maxsize=0):- LIFO (后进先出) 队列,也称为栈(Stack)。
- 元素按照添加顺序的逆序排列,获取时从队列尾部(最新添加的元素)取出。
maxsize参数用法同上。
-
queue.PriorityQueue(maxsize=0):- 优先级队列。
- 元素被赋予一个优先级,队列会确保每次取出的都是优先级最低(或最高,取决于比较方式)的元素。
- 通常,元素应该是一个元组
(priority, data),其中priority是一个可比较的数字(越小表示优先级越高)。 maxsize参数用法同上。
核心方法
这三个队列类都提供了相似的 API,以下是最常用的方法:
-
q.put(item, block=True, timeout=None):- 将
item放入队列。 - 如果
block=True(默认)且队列已满,此方法会阻塞,直到队列中有空间。 - 如果
timeout是一个正数,它会阻塞最多timeout秒。如果在超时时间内仍无法放入,会抛出queue.Full异常。 - 如果
block=False,队列满时会立即抛出queue.Full异常。
- 将
-
q.get(block=True, timeout=None):- 从队列中取出并返回一个元素。
- 如果
block=True(默认)且队列为空,此方法会阻塞,直到队列中有元素。 - 如果
timeout是一个正数,它会阻塞最多timeout秒。如果在超时时间内仍无法取出,会抛出queue.Empty异常。 - 如果
block=False,队列为空时会立即抛出queue.Empty异常。
-
q.qsize():- 返回队列中当前的元素数量。注意,这只是一个近似值,因为在多线程环境中,队列的大小可能在调用此方法和使用返回值之间发生变化。
-
q.empty():- 如果队列为空,返回
True,否则返回False。同样,这也是一个近似值。
- 如果队列为空,返回
-
q.full():- 如果队列已满,返回
True,否则返回False。这也是一个近似值。
- 如果队列已满,返回
-
q.task_done():- 此方法用于表示队列中的一个任务已经完成。
- 它通常与
q.join()一起使用。每个从get()获取的任务,最终都应该调用一次task_done()。
-
q.join():- 阻塞调用线程,直到队列中的所有任务都被处理完毕。
- 也就是说,直到每一个通过
put()放入的元素都被get()取出并对其调用了task_done()。
使用示例
1. 基本的 FIFO 队列
import queue
# 创建一个无限容量的队列
q = queue.Queue()
# 向队列中添加元素
q.put('apple')
q.put('banana')
q.put('cherry')
print(f"队列大小: {q.qsize()}") # 输出: 队列大小: 3
# 从队列中取出元素
print(q.get()) # 输出: apple
print(q.get()) # 输出: banana
print(f"队列大小: {q.qsize()}") # 输出: 队列大小: 1
print(q.get()) # 输出: cherry
# 如果队列已空,get() 会阻塞
# print(q.get()) # 程序会在这里卡住,直到有新元素被放入
2. 多线程示例 (生产者 - 消费者模式)
这是
queue 模块最经典的应用场景。import queue
import threading
import time
import random
# 创建一个最多容纳5个元素的队列
buffer = queue.Queue(maxsize=5)
def producer():
"""生产者线程: 向队列中放入数据"""
for i in range(10):
item = f"产品{i}"
try:
# 尝试将产品放入队列,如果队列满了,等待1秒
buffer.put(item, block=True, timeout=1)
print(f"生产者 {threading.current_thread().name} 生产了: {item}")
except queue.Full:
print(f"队列已满,生产者 {threading.current_thread().name} 无法生产 {item}")
# 模拟生产耗时
time.sleep(random.uniform(0.1, 0.5))
def consumer():
"""消费者线程: 从队列中取出数据并处理"""
while True:
try:
# 尝试从队列中取出产品,如果队列空了,等待2秒
item = buffer.get(block=True, timeout=2)
print(f"消费者 {threading.current_thread().name} 消费了: {item}")
# 模拟消费耗时
time.sleep(random.uniform(0.5, 1))
# 通知队列该任务已完成
buffer.task_done()
except queue.Empty:
print(f"队列已空,消费者 {threading.current_thread().name} 退出")
break
if __name__ == "__main__":
# 创建并启动生产者线程
producer_thread = threading.Thread(target=producer, name="P1")
producer_thread.start()
# 创建并启动两个消费者线程
consumer_thread1 = threading.Thread(target=consumer, name="C1")
consumer_thread2 = threading.Thread(target=consumer, name="C2")
consumer_thread1.start()
consumer_thread2.start()
# 等待生产者线程完成所有生产任务
producer_thread.join()
print("所有生产者任务已完成")
# 等待队列中所有任务被消费掉
buffer.join()
print("所有队列中的任务已被消费")
# 消费者线程在检测到队列为空后会自行退出,这里等待它们结束
consumer_thread1.join()
consumer_thread2.join()
print("所有线程结束")
代码解析:
- 生产者线程
producer不断生成产品并放入队列buffer。当队列满时 (maxsize=5),put()会阻塞或超时。 - 消费者线程
consumer不断从队列中取出产品进行 “消费”。消费完成后,必须调用buffer.task_done()来标记任务完成。 - 主线程使用
producer_thread.join()等待生产者完成。然后使用buffer.join()等待队列中所有已存在的任务都被消费掉。最后等待消费者线程结束。
3. 优先级队列 (PriorityQueue)
import queue
# 创建一个优先级队列
pq = queue.PriorityQueue()
# 放入元素,元素是 (优先级, 数据) 的元组
# 数字越小,优先级越高
pq.put((3, '任务 C'))
pq.put((1, '任务 A'))
pq.put((2, '任务 B'))
pq.put((1, '任务 A-1')) # 相同优先级的任务,按FIFO顺序取出
print("优先级队列中的任务将按优先级取出:")
while not pq.empty():
priority, task = pq.get()
print(f"处理优先级 {priority} 的任务: {task}")
pq.task_done()
# 输出顺序:
# 处理优先级 1 的任务: 任务 A
# 处理优先级 1 的任务: 任务 A-1
# 处理优先级 2 的任务: 任务 B
# 处理优先级 3 的任务: 任务 C
总结
queue 模块是 Python 中进行线程同步和数据传递的强大工具。- 线程安全:
queue模块的所有操作都是线程安全的,这意味着多个线程可以同时调用其方法而不会导致数据损坏。 - 阻塞机制:
put()和get()方法的阻塞特性使得线程可以高效地等待资源,而无需使用time.sleep()进行轮询,从而节省 CPU 资源。 - 适用场景: 最适合实现生产者 - 消费者模式,例如处理任务队列、数据 pipelines 等。
浙公网安备 33010602011771号