Python进阶教程:28_queue 队列模块 零基础超详细教程

queue 是 Python 内置的线程安全队列模块,核心作用是实现「先进先出」的数据存取,专门用来在多线程 / 多任务之间安全传递数据、调度任务。它自带锁机制,多个线程同时读写也不会出现数据错乱,是实现「生产者消费者模型」、任务排队、流量削峰的核心工具。

通俗理解:队列就像排队买奶茶 ------ 先排队的人先拿到奶茶,后到的人排在队尾;队列自动维护顺序,还能控制最大长度,满了就不让再排,空了就等新的人来。

本文从基础概念、三种核心队列、阻塞特性到经典应用,全程配可运行代码 + 逐行解释,零基础也能完全吃透。


一、前置基础概念

1. 什么是队列?

队列是一种「先进先出(FIFO = First In First Out)」的数据结构:

  • 数据从队尾加入
  • 数据从队头取出
  • 先放进去的数据,一定会先被取出来

2. queue 模块的核心价值

  • 线程安全:内置锁机制,多个线程同时读写队列,不会出现数据混乱、重复取数等问题
  • 自带阻塞机制:队列空了自动等数据,队列满了自动等空位,不用自己写等待逻辑
  • 多种队列类型:普通队列、栈、优先级队列,满足不同场景
  • 解耦程序:把「生产数据」和「处理数据」拆开,两边速度不一致也能正常运行

3. 适用场景

  • 多线程之间传递数据、分配任务
  • 爬虫:URL 任务排队、结果收集
  • 后台任务处理:比如订单处理、消息推送排队
  • 流量削峰:短时间大量请求进来,排队慢慢处理

💡 新手注意: 我们说的 queue.Queue线程间通信 用的;如果是多进程之间通信,要用 multiprocessing.Queue,两者不能混用。


二、三种核心队列类

queue 模块提供了三种常用队列,用法几乎一致,只是进出顺序不同:

队列类 规则 通俗类比 适用场景
queue.Queue 先进先出(FIFO) 排队买饭,先到先得 绝大多数任务排队、消息传递
queue.LifoQueue 后进先出(LIFO,栈) 叠盘子,最后放的最先拿 深度优先搜索、撤销功能
queue.PriorityQueue 按优先级排序,优先级高的先出 VIP 通道,级别高的先办理 任务调度、紧急任务优先处理

1. Queue:普通先进先出队列(最常用)

这是最基础、最常用的队列,严格遵守先进先出规则。

① 创建队列
python 复制代码
import queue

# 创建一个空队列,默认不限制最大长度(maxsize=0 表示无限大)
q = queue.Queue()

# 创建最多容纳 3 个元素的队列
q_limit = queue.Queue(maxsize=3)

maxsize 参数:设置队列最大元素个数,超过后再放数据会阻塞等待。


② 核心常用方法
方法 作用
q.put(item) 往队尾放入一个元素,队列满了会阻塞等待
q.get() 从队头取出一个元素,队列空了会阻塞等待
q.empty() 判断队列是否为空,返回 True/False
q.full() 判断队列是否已满,返回 True/False
q.qsize() 返回队列当前的元素个数
q.task_done() 标记一个取出的任务已经处理完成
q.join() 阻塞等待,直到队列中所有任务都被处理完

③ 基础操作完整示例
python 复制代码
import queue

# 1. 创建队列,最多放3个元素
q = queue.Queue(maxsize=3)

print("===== 放入元素 =====")
# put() 往队尾放数据
q.put("任务1")
print(f"放入任务1,当前队列长度:{q.qsize()}")
q.put("任务2")
print(f"放入任务2,当前队列长度:{q.qsize()}")
q.put("任务3")
print(f"放入任务3,队列是否已满:{q.full()}")

print("\n===== 取出元素 =====")
# get() 从队头取数据,取一个少一个
item1 = q.get()
print(f"取出:{item1},剩余长度:{q.qsize()}")
item2 = q.get()
print(f"取出:{item2},剩余长度:{q.qsize()}")
item3 = q.get()
print(f"取出:{item3},队列是否为空:{q.empty()}")

运行结果:

python 复制代码
===== 放入元素 =====
放入任务1,当前队列长度:1
放入任务2,当前队列长度:2
放入任务3,队列是否已满:True

===== 取出元素 =====
取出:任务1,剩余长度:2
取出:任务2,剩余长度:1
取出:任务3,队列是否为空:True

解释:放入顺序是 任务 1→任务 2→任务 3,取出顺序也是 任务 1→任务 2→任务 3,严格先进先出。


2. LifoQueue:后进先出队列(栈)

和普通队列正好相反:最后放进去的元素,第一个被取出来,也就是常说的「栈」结构。 用法和 Queue 完全一样,只是进出顺序不同。

示例
python 复制代码
import queue

# 创建后进先出队列
lq = queue.LifoQueue()

# 依次放入 A B C
lq.put("A")
lq.put("B")
lq.put("C")

# 依次取出
print(lq.get())  # C
print(lq.get())  # B
print(lq.get())  # A

类比:叠盘子,最后放上去的盘子,我们会第一个拿走。


3. PriorityQueue:优先级队列

给每个元素设置优先级,优先级数字越小,优先级越高,越先被取出。不是按放入顺序,而是按优先级排序出队。

用法规则
  • 放入的元素必须是元组形式(优先级数字, 数据内容)
  • 数字越小,优先级越高
  • 优先级相同的元素,会按数据内容的自然顺序排序
示例:任务优先级调度
python 复制代码
import queue

# 创建优先级队列
pq = queue.PriorityQueue()

# 放入任务:(优先级, 任务名称)
# 数字越小,优先级越高
pq.put((3, "普通任务:整理报表"))
pq.put((1, "紧急任务:修复线上bug"))
pq.put((2, "重要任务:版本上线"))
pq.put((1, "紧急任务:处理用户投诉"))

# 依次取出,优先级高的先出
while not pq.empty():
    task = pq.get()
    print(f"执行:{task[1]},优先级:{task[0]}")

运行结果:

复制代码
执行:紧急任务:修复线上bug,优先级:1
执行:紧急任务:处理用户投诉,优先级:1
执行:重要任务:版本上线,优先级:2
执行:普通任务:整理报表,优先级:3

解释:优先级 1 的紧急任务最先执行,然后是 2、3;同级别的按文字顺序排列。
⚠️ 新手坑:如果优先级相同,且后面的数据不能比较大小,会报错。所以同级数据尽量保证类型一致、可比较。


三、重点特性:阻塞与超时

队列最核心的特性就是「阻塞」,这也是它比普通列表好用的关键 ------ 不用自己写循环等待。

1. 什么是阻塞?

  • put 阻塞 :队列满了的时候,再调用 put(),程序会停在这一行,一直等,直到队列有空位了才继续执行
  • get 阻塞 :队列空了的时候,再调用 get(),程序会停在这一行,一直等,直到队列有新数据了才继续执行

新手很容易以为程序「卡死了」,其实是在正常等待数据。

2. 设置超时时间 timeout

不想一直等,可以给 putgettimeout 参数,单位是秒:

  • 超时时间内等到了:正常执行
  • 超时了还没等到:抛出对应异常
① get 超时示例
python 复制代码
import queue

q = queue.Queue()

try:
    # 最多等2秒,还没数据就抛异常
    data = q.get(timeout=2)
except queue.Empty:
    print("等了2秒,队列还是空的,没有数据")
② put 超时示例
python 复制代码
import queue

q = queue.Queue(maxsize=2)
q.put("A")
q.put("B")

try:
    # 队列满了,最多等1秒,还没空位就抛异常
    q.put("C", timeout=1)
except queue.Full:
    print("等了1秒,队列还是满的,放不进去")

3. 非阻塞模式:立刻返回,不等待

如果不想等,没位置 / 没数据直接报错,可以用非阻塞写法:

  • q.put_nowait(item) 等价于 q.put(item, block=False)
  • q.get_nowait() 等价于 q.get(block=False)

满了 / 空了会直接抛出 queue.Full / queue.Empty 异常,适合能接受失败、自己做重试的场景。


四、经典应用:生产者消费者模型

「生产者消费者模型」是队列最经典的应用场景,也是多线程编程的核心模式:

  • 生产者:负责生成任务 / 数据,放进队列
  • 消费者:负责从队列里取任务,处理任务
  • 队列:中间的缓冲区,解耦生产和消费,平衡两边的速度

比如爬虫场景:

  • 生产者线程:负责发现新的 URL,放进 URL 队列
  • 消费者线程:从队列里取 URL,下载页面、解析数据

完整实战示例

python 复制代码
import queue
import threading
import time
import random

# 1. 创建任务队列,最多放10个任务
task_queue = queue.Queue(maxsize=10)

# 2. 生产者函数:生成任务,放进队列
def producer(name):
    for i in range(5):
        task = f"{name}-任务{i}"
        task_queue.put(task)
        print(f"✅ {name} 生产了:{task},队列长度:{task_queue.qsize()}")
        # 模拟生产速度随机
        time.sleep(random.uniform(0.2, 0.5))

# 3. 消费者函数:从队列取任务,处理
def consumer(name):
    while True:
        # 从队列取任务,没任务就阻塞等
        task = task_queue.get()
        print(f"⚙️  {name} 处理了:{task}")
        # 模拟处理耗时
        time.sleep(random.uniform(0.3, 0.7))
        # 标记任务处理完成
        task_queue.task_done()

# 4. 启动线程
if __name__ == "__main__":
    # 启动2个生产者
    threading.Thread(target=producer, args=("生产者A",)).start()
    threading.Thread(target=producer, args=("生产者B",)).start()

    # 启动3个消费者,设为守护线程,主线程结束就自动结束
    for i in range(3):
        t = threading.Thread(target=consumer, args=(f"消费者{i+1}",), daemon=True)
        t.start()

    # 等待队列所有任务都处理完成
    task_queue.join()
    print("\n🎉 所有任务全部处理完成!")

代码逐行解释

  1. task_done():每处理完一个任务,必须调用一次,告诉队列「这个任务我做完了」
  2. join():阻塞等待,直到队列里所有任务都被标记为完成,才继续往下执行
  3. 守护线程 daemon=True:主线程结束后,消费者线程自动退出,不会一直卡在那等
  4. 解耦效果:生产者生产快、消费者处理慢也没关系,任务在队列里排队慢慢处理,不会丢失

五、新手高频易错点总结

1. 为什么不用普通列表 list?

很多新手会问:用 append() 加元素、pop(0) 取元素,不也能实现队列吗?

  • 效率问题list.pop(0) 会把所有元素往前移,数据量大的时候速度很慢
  • 线程安全:普通列表没有锁,多线程同时读写会出现数据错乱、重复取数等 bug
  • 功能缺失:列表没有阻塞、超时、join 等高级功能,都要自己写

2. 队列空了程序卡住?

不是 bug,是 get() 的阻塞特性在等数据。

  • 确定数据全部生产完了:用 join() 等待所有任务处理完
  • 不想一直等:加 timeout 参数,或者用 get_nowait()

3. PriorityQueue 优先级搞反

记住:数字越小,优先级越高。想让任务先执行,就给更小的数字。

4. 多进程用不了 queue.Queue

queue.Queue 只在同一个进程的多线程之间安全;多进程通信要用 multiprocessing.Queue,不然数据不通。

5. 忘记调用 task_done ()

如果用了 join() 等待,每个任务处理完必须调用 task_done(),否则 join() 会永远等下去,程序卡死。

相关推荐
司小豆1 小时前
第七课:DeepSeek Harness 服务与依赖注入
java·服务器·开发语言·github·ai编程
小柯南敲键盘1 小时前
跨马翻译AI工具,批量图片视频翻译与智能抠图一站式解决
人工智能·python·音视频
veminhe1 小时前
python对象的序列化和反序列化
python
weixin199701080161 小时前
[特殊字符]《闲鱼 + 淘宝 + 1688 三平台库存同源:二手ERP主数据治理与超卖防御》(附Python源码)
开发语言·python
ltqshs1 小时前
C语言-链表例程
c语言·开发语言·链表
shmily麻瓜小菜鸡1 小时前
JavaScript / TypeScript 易踩坑知识点 —— 作用域与变量类
开发语言·javascript·typescript
YYYing.1 小时前
【C++进阶系列 (一)】关于线程堆栈的那些事 (上篇)
c语言·开发语言·c++·线程堆栈
zh73141 小时前
Go 1.23 → 1.26 升级检查文档
开发语言·chrome·golang
许彰午1 小时前
24-MyBatisHelper与autoCount
java·低代码·架构