asyncio可通过PriorityQueue实现优先级调度,以(priority, coro)元组入队,数值越小优先级越高;需避免不可比较对象、异常隔离及动态优先级调整。

asyncio 本身不提供原生优先级调度,但通过合理封装任务和控制入队顺序,完全可以实现可靠、可预测的优先级执行。关键不在“改事件循环”,而在“管好任务怎么进、谁先出”。
用 asyncio.PriorityQueue 做最简优先调度
这是最直接、最轻量的方式。它底层基于 heapq,自动按元组第一个元素(优先级值)排序,数值越小,越早被 get() 取出。
- 提交任务时传入 (priority, coro) 元组,比如
await queue.put((1, fetch_user()))表示高优,(5, backup_logs())表示低优 - 协程对象必须是未 await 的,即
fetch_user()而不是await fetch_user(),否则调度器执行时会报错 - 多个任务优先级相同时(如都为 2),执行顺序不确定——这是 heapq 的默认行为,无需额外干预,除非业务强依赖 FIFO
避免不可比较对象引发崩溃
如果任务里混用了字典、列表等不可比较类型,直接用 (priority, task_data) 会触发 TypeError。稳妥做法是加一个唯一标识:
- 用
(priority, id(coro), coro)包装,确保堆内元素总能比大小 - 或者统一用
dataclass封装任务,定义__lt__方法明确比较逻辑 - 别用字符串或枚举当 priority 字段,数字最安全,也最符合直觉(1 比 10 高)
让调度器更健壮:异常隔离 + 状态感知
一个失败任务不该拖垮整个调度循环。worker 协程要主动捕获异常,并确保 task_done() 总被执行:
- 用
try/finally包裹await coro,保证无论成功失败都调用queue.task_done() - 在
except Exception块里记录日志或上报错误,而不是抛出——否则await queue.join()可能永远卡住 - 如果需要区分任务完成状态(成功/失败/超时),可在任务包装时附带回调函数或 future 对象
真实场景中优先级不只是数字大小
静态整数优先级适合简单分层,但复杂系统往往需要动态调整:
- 等待超时的任务可自动提升一级优先级,防止饥饿,比如在 get() 逻辑里检查入队时间戳
- 用户关键操作(如支付确认)设为 0,后台同步设为 10,告警通知设为 -1,形成语义清晰的层级
- 不要把优先级和耗时挂钩——短任务不一定高优,长任务也不一定低优;优先级反映的是业务重要性,不是执行成本

















