Python queue
模块使用教程
目录
1. queue模块概述
Python的queue
模块提供了线程安全的队列实现,主要用于多线程编程环境中安全地传递消息或数据。它是Python标准库的一部分,无需额外安装。
主要特点:
- 线程安全:所有方法都是原子操作
- 阻塞操作:支持阻塞式put/get
- 超时控制:可以设置操作超时时间
- 大小限制:可以限制队列容量
2. 基础队列类型
Queue (FIFO队列)
基本先进先出队列:
import queue
q = queue.Queue(maxsize=5) # 创建容量为5的队列
# 基本操作
q.put('item1')
q.put('item2')
print(q.get()) # 输出: item1
print(q.get()) # 输出: item2
LifoQueue (LIFO队列/栈)
后进先出队列(栈结构):
import queue
lq = queue.LifoQueue()
lq.put('first')
lq.put('second')
print(lq.get()) # 输出: second (后进先出)
print(lq.get()) # 输出: first
PriorityQueue (优先级队列)
按优先级顺序取出的队列:
import queue
pq = queue.PriorityQueue()
pq.put((3, 'Low priority'))
pq.put((1, 'High priority'))
pq.put((2, 'Medium priority'))
print(pq.get()[1]) # 输出: High priority
print(pq.get()[1]) # 输出: Medium priority
print(pq.get()[1]) # 输出: Low priority
SimpleQueue (简单的FIFO队列)
无界的 FIFO 队列构造函数。简单的队列,缺少任务跟踪等高级功能。这个特殊实现为小功能在交换中提供额外的保障。
3. 核心方法
put() 方法
向队列中添加项目:
q.put(item, block=True, timeout=None)
参数:
block
: 当队列满时是否阻塞(默认True)timeout
: 阻塞超时时间(秒)
示例:
try:
q.put('item', block=True, timeout=2) # 最多等待2秒
except queue.Full:
print("队列已满,无法添加")
get() 方法
从队列中获取并移除项目:
item = q.get(block=True, timeout=None)
参数同put()
方法。
示例:
try:
item = q.get(block=True, timeout=3) # 最多等待3秒
except queue.Empty:
print("队列为空,无法获取")
其他关键方法
-
qsize() - 返回队列的近似大小
print(q.qsize()) # 注意:在多线程环境中不精确
-
empty() - 判断队列是否为空
if not q.empty(): item = q.get()
-
full() - 判断队列是否已满
if not q.full(): q.put(item)
-
task_done() - 标记任务完成
q.get() # 处理任务... q.task_done() # 告诉队列任务处理完成
-
join() - 阻塞直到所有任务完成
q.join() # 阻塞直到所有任务被处理并标记为done
4. 队列阻塞与超时
队列操作默认是阻塞的,但可以控制:
# 非阻塞put
try:
q.put(item, block=False) # 立即返回,如果队列满则抛出queue.Full
except queue.Full:
print("队列已满")
# 非阻塞get
try:
item = q.get(block=False) # 立即返回,如果队列空则抛出queue.Empty
except queue.Empty:
print("队列为空")
# 带超时的操作
try:
q.put(item, timeout=2.5) # 最多等待2.5秒
item = q.get(timeout=1.0) # 最多等待1秒
except queue.Full:
print("放入超时")
except queue.Empty:
print("获取超时")
5. 队列大小控制
创建队列时可以设置最大容量:
q = queue.Queue(maxsize=100) # 最多容纳100个元素
maxsize=0
(默认):无限容量maxsize>0
:有限容量,put可能阻塞maxsize<0
:等同于0,无限容量
6. 多线程安全特性
queue
模块的所有实现都是线程安全的:
import threading
import queue
def worker(q):
while True:
item = q.get()
print(f"处理: {item}")
q.task_done()
q = queue.Queue()
threads = []
for i in range(3):
t = threading.Thread(target=worker, args=(q,))
t.daemon = True
t.start()
threads.append(t)
for item in range(10):
q.put(item)
q.join() # 等待所有任务完成
7. 高级使用技巧
自定义优先级规则
import queue
class CustomPriorityQueue(queue.PriorityQueue):
def _put(self, item):
# 自定义优先级逻辑
priority = -item[0] # 反转优先级
super()._put((priority, item[1]))
pq = CustomPriorityQueue()
pq.put((3, 'Low'))
pq.put((1, 'High'))
pq.put((2, 'Medium'))
print(pq.get()[1]) # 输出: Low (因为优先级被反转)
队列任务跟踪
q = queue.Queue()
def monitored_put(item):
print(f"准备放入: {item}")
q.put(item)
print(f"已放入: {item}, 队列大小: {q.qsize()}")
def monitored_get():
print(f"尝试获取, 队列大小: {q.qsize()}")
item = q.get()
print(f"已获取: {item}, 队列大小: {q.qsize()}")
return item
生产者-消费者模式
import queue
import threading
import random
import time
def producer(q, id):
for i in range(5):
item = f"产品-{id}-{i}"
q.put(item)
print(f"生产者 {id} 生产了 {item}")
time.sleep(random.random())
def consumer(q, id):
while True:
item = q.get()
if item is None: # 终止信号
break
print(f"消费者 {id} 消费了 {item}")
q.task_done()
time.sleep(random.random() * 2)
q = queue.Queue()
producers = [threading.Thread(target=producer, args=(q, i)) for i in range(3)]
consumers = [threading.Thread(target=consumer, args=(q, i)) for i in range(2)]
for t in producers + consumers:
t.start()
for t in producers:
t.join()
# 发送终止信号
for _ in consumers:
q.put(None)
for t in consumers:
t.join()
8. 常见问题
问题1:队列卡死
现象:程序卡在put()
或get()
操作上
解决方案:
- 设置合理的超时时间
- 确保所有线程最终都会处理队列
- 使用
qsize()
监控队列状态
问题2:任务未正确标记完成
现象:join()
永远阻塞
解决方案:
- 确保每个
get()
后都有对应的task_done()
- 使用try-finally确保标记:
item = q.get()
try:
# 处理item
finally:
q.task_done()
问题3:优先级队列排序错误
现象:元素未按预期顺序取出
解决方案:
- 确保优先级是可比较的
- 对于复杂对象,实现
__lt__
方法或使用元组(priority, data)
9. 性能优化
-
合理设置队列大小:
- 太大消耗内存
- 太小容易阻塞
-
批量操作:
- 合并小任务为大任务减少队列操作
-
避免频繁查询:
- 减少
empty()
/full()
调用,直接使用try-catch
- 减少
-
选择合适的队列类型:
- 默认FIFO满足大多数场景
- 特殊需求选择LIFO或PriorityQueue
-
线程池配合:
- 结合
concurrent.futures.ThreadPoolExecutor
使用
- 结合
10. 注意事项
-
资源清理:
- 确保所有线程都能正确退出
- 使用哨兵值(如None)通知消费者线程退出
-
错误处理:
- 总是处理
queue.Full
和queue.Empty
异常 - 记录队列操作失败的情况
- 总是处理
-
监控队列:
- 记录队列大小变化
- 监控任务处理时间
-
合理设计:
- 生产者速率应与消费者处理能力匹配
- 考虑使用多个队列分离不同优先级任务
-
测试策略:
- 模拟队列满/空的情况
- 测试多线程竞争条件
-
替代方案:
- 对于简单场景,考虑使用
collections.deque
- 对于跨进程通信,使用
multiprocessing.Queue
- 对于简单场景,考虑使用