asyncio.Queue 中协程等待任务的本质;RAG 任务中断恢复机制改造总结;JPEG 流接入海康综合安防平台(GB28181协议);

asyncio.Queue 中协程等待任务的本质

核心结论

asyncio 中没有真正的"线程阻塞",只有协程级别的"挂起"(suspend)。
当多个 worker 协程执行 await queue.get() 而队列为空时,等待的是协程本身,而不是操作系统线程。

等待发生在哪里

  • 阻塞点在各个消费者 _worker_loop 里的 await self._queue.get()
  • 队列为空时,每个调用 get() 的协程会各自创建一个内部 Future
  • 这个 Future 被加入队列内部的等待者列表(_getters
  • 协程在 await 处挂起,把控制权交还给事件循环

挂起 ≠ 阻塞线程

  • 事件循环所在的线程并不会被卡住
  • 挂起期间,事件循环可以继续调度其他就绪的协程
    (比如别的 worker、FastAPI 的请求处理协程等)
  • 这和 time.sleep() 或同步 queue.Queue.get() 那种真正阻塞线程的调用完全不同

如何被唤醒

  • 当生产者往队列里 put 数据时,队列内部从等待者列表中
    按先进先出顺序取出一个 Future 并标记完成
  • 这是"一对一唤醒",不是广播唤醒所有等待者
  • 因此多个空闲 worker 之间是公平排队领任务,不会出现惊群效应

与取消(cancel)的关系

  • 如果某个 worker 正挂起在 queue.get()
  • 调用 task.cancel() 会让这次 await 直接抛出 CancelledError
  • 这也是为什么 _worker_loop 内部通常需要捕获
    CancelledError 来做退出前的清理工作

一句话总结

所有空闲的 worker 协程都在"排队等待被叫号",
等待的是协程的挂起状态,而非线程被锁住。


RAG 任务中断恢复机制改造总结

问题起点

最初用 asyncio.Event 让 worker 协程在中断点原地挂起,等待人工审批后被唤醒。
这种方式会让 worker 长期被单个任务占用,并发能力随之下降;
而且所有等待状态都存在内存里,一旦进程重启就会丢失。

改造方向一:从“原地等待”转为“中断即释放、resume 即新任务”

不再让 worker 卡在中断点等待,而是中断发生时直接把任务标记为完成中断、
worker 随即被释放去处理别的任务。
用户做出审批决定后,恢复操作不是去“唤醒”原来的执行流程,
而是重新发起一个全新的任务,由任意空闲 worker 接手执行。
新旧两次执行之间通过 LangGraph 的 thread_id(也就是业务里的 session_id)联系起来,
执行状态的真正延续依赖的是 LangGraph 自身的 checkpointer 机制,
而不是某个具体 worker 协程还“记得”之前跑到哪了。

改造方向二:dispatcher 不再掺和业务语义,回归纯调度

一开始的方案里,恢复请求需要先按旧任务 ID 查一遍历史记录,
取出之前保存的 session_id 才能继续。
后来意识到 session_id 本身就是前端最早生成、传进来的,
没有必要让后端专门维护一条记录、绕一圈再找回这个值。
于是把这件事简化掉:恢复请求由前端直接带上 session_id 和审批结果,
dispatcher 不再需要知道“这是不是一次恢复”,也不用记住任何和恢复相关的状态,
彻底退化成一个只管排队和执行的通用调度器,不掺和具体业务逻辑。
这样做换来的代价是,原本在调度层做的“这个任务真的在等审批吗”这类校验没有了,
但这类校验更应该交给 LangGraph 在真正执行续跑时自然处理,而不是靠调度器越权代劳。

改造方向三:工作流状态查询方式

架构里根本不需要调度程序TaskStatus、interrupt_payload这些字段去给前端“模拟”一个状态接口
直接调用graph.get_state(config)去问LangGraph“这个线程现在不是卡在中断点”。

JPEG 流接入海康综合安防平台(GB28181协议)

整体流程

JPEG → 解码 → H.264/H.265 编码 → PS 封装 → RTP 封装 → SIP(GB28181) → 海康综合安防平台

关键知识

  • SIP:设备注册、鉴权、心跳、Invite 等信令。
  • RTP:负责传输音视频数据。
  • PS:封装 H.264 数据。
  • H.264:视频编码。

FFmpeg 的作用

负责媒体处理:

  • JPEG 解码
  • H.264/H.265 编码

不负责: SIP 注册、KeepAlive 心跳、Invite、设备管理(Catalog、DeviceInfo 等)
因此,FFmpeg 不能单独完成 GB28181 推流。

推荐架构

JPEG流 → FFmpeg(JPEG → H.264) → GB28181 SDK/网关(PS + RTP + SIP) → 海康综合安防平台

职责划分:

  • FFmpeg:负责媒体编解码。
  • GB28181 SDK/网关:负责协议封装(PS、RTP)和 SIP 信令。
posted @ 2026-06-30 17:21  asphyxiasea  阅读(7)  评论(0)    收藏  举报