asyncio本身不支持任务优先级,需用asyncio.priorityqueue配合调度协程实现:优先级值越小越先执行,调度器循环await取出并运行协程,注意协程对象传递、异常隔离与task_done调用。

asyncio 里怎么给任务设优先级
asyncio 本身不支持任务优先级——asyncio.create_task() 和 asyncio.ensure_future() 提交的任务,全靠事件循环统一调度,没有内置的 priority 参数或队列分级机制。想实现优先级,得自己搭一层调度逻辑。
常见错误是试图用 asyncio.sleep(0) 或 await asyncio.yield_()(不存在)来“让出低优任务”,这没用:它只影响当前协程让渡时机,不改变任务在就绪队列里的顺序。
- 真正可行的路只有一条:用带优先级的队列(如
asyncio.PriorityQueue)集中管理待执行协程,再由一个长期运行的调度协程按序await它们 -
asyncio.PriorityQueue是线程安全的,但注意:它的get()是协程方法,必须await,不能直接调用 - 优先级值越小,越先被取出;通常用整数,比如高优任务塞
(1, coro),低优塞(10, coro)
PriorityQueue + 调度协程的最小可行写法
别碰复杂的自定义事件循环或 monkey patch asyncio 内部——容易崩且不可维护。下面这个结构够用、易测、不侵入原逻辑:
import asyncio
<p>async def scheduler(queue: asyncio.PriorityQueue):
while True:
priority, coro = await queue.get()
try:
await coro
except Exception:</p><h1>别让一个任务失败杀掉整个调度器</h1><pre class='brush:python;toolbar:false;'> pass
finally:
queue.task_done()启动调度器(只启一个)
queue = asyncio.PriorityQueue() asyncio.create_task(scheduler(queue))
提交任务:高优先执行
await queue.put((1, some_high_priority_coro())) await queue.put((5, some_low_priority_coro()))
注意:some_high_priority_coro() 必须是协程对象(即调用后返回 coroutine,不是已 await 的结果),否则 await coro 会报 TypeError: object XXX can't be used in 'await' expression。
立即学习“Python免费学习笔记(深入)”;
为什么不用 asyncio.to_thread() 或 run_in_executor 做优先级
有人想把高优任务扔进线程池,靠 OS 线程调度抢资源——这反而破坏 async 模型,还引入同步阻塞风险。更关键的是:asyncio.to_thread() 和 loop.run_in_executor() 返回的是 Task,你没法控制它在 executor 内部的排队顺序,Python 的 concurrent.futures.ThreadPoolExecutor 本身也不暴露优先级接口。
- 优先级逻辑必须落在协程提交到事件循环前的那层,而不是执行时
- 如果真有 CPU 密集型高优任务,正确做法是:提前在协程里做轻量预判 → 把真正耗 CPU 的部分拆出来 → 用
asyncio.to_thread()执行,但优先级判断仍由主调度协程完成 - 混用线程和协程优先级,等于两套调度打架,最后谁快谁慢取决于系统负载,不可控
容易被忽略的边界:cancel() 和异常传播
用 PriorityQueue 调度时,任务还没 await 就被取消,不会触发任何异常;但一旦开始 await coro,取消信号才能传进去。这意味着:
- 高优任务正在运行时,低优任务即使已入队,也得等它结束——
PriorityQueue只管“取”,不管“中断” - 如果需要抢占式中断(比如用户取消请求),得在协程内部定期检查
asyncio.current_task().cancelled(),并手动清理资源 -
queue.task_done()必须在try/finally里调用,否则某次异常未捕获会导致queue.join()永远卡住
实际项目里,最麻烦的不是怎么排优先级,而是怎么定义“优先级变化”——比如用户操作中途降权、后台任务因资源不足被动态降级。这些逻辑得收口到调度器外,靠状态机驱动,而不是堆更多 if-else 在 put() 调用点。










