动态插入异步任务的关键是维持队列长期存活并解耦生产与消费:依托asyncio.Queue与常驻消费者协程,支持HTTP请求、定时器、消息通知等实时场景随时入队,需正确调用task_done()并用join()确保任务完成。

动态插入异步任务的关键,是让队列持续存活、生产与消费解耦、且不依赖任务提交时机。只要队列对象还在运行中,任何协程或回调都能随时 await queue.put(task) —— 不需要重启服务,也不用重新初始化消费者。
核心机制:asyncio.Queue + 常驻消费者
asyncio.Queue 是协程原生支持的线程安全队列,它天然适配事件循环。put 和 get 操作会自动挂起协程,不阻塞事件循环本身。消费者必须是长期运行的协程(例如用 asyncio.create_task(worker(queue)) 启动),内部用 while True: task = await queue.get() 持续监听,处理完调用 await queue.task_done()。
- 队列可设
maxsize防止内存溢出,超限时put()自动等待空位 - 多个消费者并发
get(),Queue 内部自动负载均衡 - 任务可以是函数调用、协程对象,或带上下文的字典/数据类
动态插入的典型场景
任务不是启动时就全部确定的,而是随业务实时产生:HTTP 请求触发、定时器回调、消息中间件通知、数据库变更监听等。只要持有队列引用,这些地方都能直接 await queue.put(...)。
- Web 请求中:用户上传文件后,立即投递「转码」任务到队列
- 定时任务中:每分钟扫描新订单,为每个订单生成「发短信」子任务
- 事件驱动中:Kafka 消费到一条日志,封装为清洗任务入队
关闭与等待:避免任务丢失
停止新增任务后,不能直接退出。需先确保所有已入队任务执行完毕:
- 调用
await queue.join(),它会阻塞直到所有task_done()被调用 - 消费者协程应配合退出信号(如
asyncio.Event或哨兵值)主动跳出循环 - 最后可选择 cancel 消费者任务,但前提是已无待处理任务
常见误区提醒
不是每次任务都启一个新协程,那样会失控;也不是把队列做成局部变量,否则作用域一结束就不可用了。队列应作为共享对象在应用生命周期内长期存在,比如挂载在 FastAPI 的 app.state 或作为全局单例管理。
- 错误:在每次请求里 new 一个 Queue → 任务无法被已有消费者获取
- 错误:用普通 queue.Queue → 在协程中调用 get() 会阻塞整个事件循环
- 错误:忘记调用 task_done() → join() 永远不会返回

















