GUI/HTTP/引擎如何共用一条任务队列
第一性原理:一个任务源只需要一个队列
做桌面工具,经常同时有两个入口需要处理任务:
- 前台 GUI 上用户点"开始处理"
- 后台 HTTP 接口收到文件上传请求
这两个入口如果各自开队列、各自管理引擎,不仅代码重复,还会让同一个进程里多线程抢占 IO,出很多莫名其妙的锁问题。
本质上,这俩都是任务生产者 ,真正干活的只有一个识别引擎。所以用第一性原理看问题:任务只有一个来源,那就只要一个队列。
代码实现
我们项目里就是这么做的:
python
# main_window.py: 应用启动时只创建一次
self.queue = TaskQueue(maxsize=self.config.queue_maxsize)
# 引擎是单例
self.engine = Engine(self.config, self.bridge)
# 队列消费者绑定到引擎.process
if self.config.engine_auto_start:
self.queue.start(self.engine.process)
GUI 入口提交任务(direct_page.py):
python
folder = self.folder_edit.text().strip()
self.queue.submit({"p_FolderPath": folder})
HTTP 入口提交任务(http_server.py):
python
# FastAPI 接收到上传,把任务推进同一个队列
item = {
"p_FolderPath": temp_dir,
"p_TaskId": task_id,
"template": template,
}
if not queue.submit(item):
return {"code": 503, "msg": "队列已满,请稍后重试"}
就这么简单。GUI 和 HTTP 都是生产者,引擎是唯一消费者。
好处
- 一份代码,两处复用:不用给 GUI 写一套,HTTP 再写一套。
- 天然流控:队列满了新任务直接拒绝,保护内存不爆。
- 状态统一:引擎挂了就是挂了,不管从哪进来都进不去,不用两边同步状态。
关键约束
要做到这一点,任务必须是可序列化的纯字典,不能把 GUI 对象、Request 对象塞进去。我们这里任务就只带路径、参数、任务ID,全部是字符串,跨线程传递完全安全。