Python队列(Queue)2 —— heapq模块与queue.PriorityQueue

相关链接
1. 队列和栈
2. heapq模块与queue.PriorityQueue
3. 多任务列表

Python heapq 模块与 queue.PriorityQueue 对比解析

一、核心区别总结

特性heapq 模块queue.PriorityQueue
线程安全非线程安全,需手动加锁❌线程安全,内部通过锁机制实现✔️
实现层级底层堆操作(需手动维护堆结构)高层封装(基于 heapq 实现,简化操作)
适用场景单线程高性能需求或需自定义堆逻辑多线程环境下的优先级任务调度
任务跟踪支持支持 task_done() 和 join() 方法
接口复杂度需直接操作列表,调用函数(如 heappush(), heappop())提供队列式 API(put()/get())
适用场景单线程环境多线程环境
性能更高(无锁开销)稍低(因线程安全开销)

二、功能对比详解

1. 线程安全性

  • heapq: 不提供线程安全机制,需开发者自行加锁(如 threading.Lock)确保多线程安全。
  • PriorityQueue: 内部集成锁机制,put() 和 get() 操作自动保证线程安全。

2. 接口设计

  • heapq: 直接操作列表,需调用函数维护堆属性:
import heapq
heap = []
heapq.heappush(heap, (2, "任务A"))  # 插入元素
item = heapq.heappop(heap)          # 弹出最小元素
  • PriorityQueue: 封装队列接口,操作更直观:
from queue import PriorityQueue
pq = PriorityQueue()
pq.put((2, "任务A"))  # 插入元素
item = pq.get()        # 弹出最小元素

3. 任务完成跟踪

  • PriorityQueue: 继承自 Queue 类,支持任务完成标记与同步:
pq.put((1, "任务B"))
pq.get()
pq.task_done()  # 标记任务完成
pq.join()       # 阻塞直到所有任务完成
  • heapq: 无内置支持,需手动实现计数器或事件机制。

4. 性能差异

  • 单线程场景: heapq 更高效(无锁竞争)。
  • 多线程场景: PriorityQueue 更安全,但锁机制带来轻微性能损耗。

5. 优先级更新与动态调整

  • 两者均不支持直接修改已有元素的优先级。需通过插入新元素间接实现:
# 示例:更新任务优先级(均需插入新条目)
heapq.heappush(heap, (new_priority, "任务A"))  # heapq
pq.put((new_priority, "任务A"))               # PriorityQueue

三、使用场景推荐

1. 选择 heapq 的情况

  • 单线程环境,需极致性能优化(如高频插入/删除操作)。
  • 需要灵活控制堆结构或实现自定义堆逻辑(如最大堆、复杂排序规则)。
  • 无需任务完成跟踪,且可接受手动维护线程安全。

2. 选择 PriorityQueue 的情况

  • 多线程程序,需线程安全的优先级队列。
  • 需要简洁的队列式 API(put/get)及任务完成跟踪功能。
  • 对性能要求不苛刻,可接受轻微锁开销。

四、代码示例

1. heapq 实现最小堆

import heapq

# 初始化堆
tasks = []
heapq.heappush(tasks, (3, "低优先级任务"))
heapq.heappush(tasks, (1, "高优先级任务"))

# 弹出最小元素
while tasks:
    priority, task = heapq.heappop(tasks)
    print(f"处理任务: {task} (优先级 {priority})")

2. PriorityQueue 多线程任务调度

from queue import PriorityQueue
import threading

def worker(pq):
    while True:
        priority, task = pq.get()
        if task is None:  # 终止信号
            pq.task_done()
            break
        print(f"处理任务: {task} (优先级 {priority})")
        pq.task_done()

pq = PriorityQueue()
# 启动消费者线程
thread = threading.Thread(target=worker, args=(pq,))
thread.start()

# 生产者添加任务
pq.put((2, "任务A"))
pq.put((1, "任务B"))
pq.put((3, "任务C"))

# 发送终止信号并等待完成
pq.put((0, None))
thread.join()

3. PriorityQueue (多线程爬虫任务监控):

import queue
import threading

# 创建任务队列与任务类
task_queue = queue.Queue()

class Task:
    def __init__(self, task_id):
        self.task_id = task_id
        self.status = "pending"
    def complete(self):
        self.status = "completed"
    def fail(self):
        self.status = "failed"

# 生产者添加任务
task1 = Task(1)
task_queue.put(task1)

# 消费者处理任务
def worker():
    while True:
        task = task_queue.get()
        try:
            # 模拟任务处理
            task.complete()
        except Exception:
            task.fail()
        task_queue.task_done()

# 启动消费者线程
thread = threading.Thread(target=worker)
thread.start()

# 主线程等待所有任务完成
task_queue.join()
print("所有任务处理完毕")

4. 基于 heapq 的任务状态跟踪

适用于基于 heapq 或自定义队列的场景,需结合计数器或回调函数。

实现步骤:
  1. 定义任务状态字段:如 status(pending/completed/failed)。
  2. 维护计数器或事件对象:记录已完成任务数,或通过事件通知主线程。
  3. 轮询或回调机制:定期检查任务状态或任务完成时触发回调。
import heapq
from collections import defaultdict

class TaskTracker:
    def __init__(self):
        self.heap = []
        self.task_status = defaultdict(str)  # 存储任务状态
    
    def add_task(self, task_id, priority):
        heapq.heappush(self.heap, (priority, task_id))
        self.task_status[task_id] = "pending"
    
    def complete_task(self, task_id):
        self.task_status[task_id] = "completed"
    
    def all_done(self):
        return all(status == "completed" for status in self.task_status.values())

# 使用示例
tracker = TaskTracker()
tracker.add_task("task1", 2)
tracker.add_task("task2", 1)

# 处理任务并更新状态
tracker.complete_task("task2")
print(f"所有任务完成: {tracker.all_done()}")  # 输出: False

五、PriorityQueue 基本用法

1. 定义与基础操作

import queue

# 创建优先级队列(默认无界,可通过 maxsize 限制容量)
pq = queue.PriorityQueue()

# 插入元素(格式:(priority, data))
pq.put((3, "Low Priority Task"))
pq.put((1, "High Priority Task"))
pq.put((2, "Medium Priority Task"))

# 按优先级取出元素
while not pq.empty():
    priority, task = pq.get()
    print(f"执行任务:{task}(优先级 {priority})")

输出:

执行任务:High Priority Task(优先级 1)
执行任务:Medium Priority Task(优先级 2)
执行任务:Low Priority Task(优先级 3)

2. 处理相同优先级

当多个元素优先级相同时,默认按插入顺序处理(依赖元素的比较规则):

pq = queue.PriorityQueue()
pq.put((1, "TaskA"))  # 先插入
pq.put((1, "TaskB"))  # 后插入

# 若直接使用 (priority, data),可能因数据不可比较而报错!
# 推荐添加插入顺序作为辅助比较字段
pq.put((1, 0, "TaskA"))  # (priority, order, data)
pq.put((1, 1, "TaskB"))

while not pq.empty():
    print(pq.get())  # 输出顺序:TaskA → TaskB
  • 关键方法

方法说明
put((priority, item))插入元素,队列满时阻塞
get()取出优先级最高的元素,队列空时阻塞
empty()判断队列是否为空
full()判断队列是否已满
qsize()返回队列当前元素数量(多线程下结果可能不精确)
task_done()消费者处理完任务后调用,标记任务完成。
join()阻塞主线程,直到队列中所有任务完成(需所有 get() 后调用 task_done())。

六、heapq模块基本用法

heapq 是 Python 标准库中用于实现堆数据结构(优先队列算法)的模块,默认构造最小堆(父节点值 ≤ 子节点值),支持高效的最小元素访问与堆调整操作,适用于单线程场景。

1. 核心方法及功能

  • 堆初始化与转换

heapq.heapify(x)
将列表 x 原地转换为堆结构(O(n) 时间复杂度),满足堆属性 heap[k] ≤ heap[2k+1] 且 heap[k] ≤ heap[2k+2]。

import heapq
lst = [3, 1, 4, 1, 5]
heapq.heapify(lst)  # lst变为堆结构:[1, 1, 4, 3, 5]
  • heapq.heappushpop(heap, item)
    先插入 item 再弹出最小元素,性能优于先 heappush 后 heappop。
  • 元素访问与删除

heapq.heappop(heap)
弹出并返回堆中最小元素(根节点 heap),剩余元素自动调整为堆。

min_val = heapq.heappop(lst)  # 返回1,堆变为 [1, 3, 4, 5]

heapq.heappushpop(heap, item)
先插入 item 再弹出最小元素,性能优于先 heappush 后 heappop。

  • 高效操作函数

heapq.heapreplace(heap, item)
弹出最小元素并插入新元素,保持堆大小不变(需堆非空)。
heapq.nlargest(n, iterable) / nsmallest(n, iterable)
返回可迭代对象中最大或最小的 n 个元素(无需完全排序)
heapq.merge(*iterables)
合并多个已排序的可迭代对象,生成一个有序迭代器。

  • 应用场景, 优先队列实现

通过最小堆快速处理高优先级任务(如实时任务调度)。

tasks = []
heapq.heappush(tasks, (2, "低优先级任务"))
heapq.heappush(tasks, (1, "高优先级任务"))
heapq.heappop(tasks)  # 返回 (1, "高优先级任务")

上一篇:

1. 队列和栈

下一篇:

 3. 多任务列表

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值