[英]Using Multiprocessing in Uvicorn with multiple workers (thread lock)
我正在构建一个 API,它通过 uvicorn 提供 FastAPI。 API 具有使用 python 多处理库的端点。
端点为 CPU 绑定任务生成多个进程以并行执行它们。 这是高级代码逻辑概述:
import multiprocessing as mp
class Compute:
def single_compute(self, single_comp_data):
# Computational Task CPU BOUND
global queue
queue.put(self.compute(single_comp_data))
def multi_compute(self, task_ids):
# Prepare for Compuation
output = {}
processes = []
global queue
queue = mp.Queue()
# Start Test Objs Computation
for tid in task_ids:
# Load task data here, to make use of object in memory cache
single_comp_data = self.load_data_from_cache(tid)
p = mp.Process(target=self.single_compute, args=single_comp_data)
p.start()
processes.append(p)
# Collect Parallel Computation
for p in processes:
result = queue.get()
output[result["tid"]]= result
p.join()
return output
这是简单的 API 代码:
from fastapi import FastAPI, Response
import json
app = FastAPI()
#comp holds an in memory cache, thats why its created in global scope
comp = Compute()
@app.get("/compute")
def compute(task_ids):
result = comp.multi_compute(task_ids)
return Response(content=json.dumps(result, default=str), media_type="application/json")
当像这样与多个工人一起运行时:
uvicorn compute_api:app --host 0.0.0.0 --port 7000 --workers 2
我收到此 python 错误
TypeError: can't pickle _thread.lock objects
只有 1 个工作进程就可以了。 该程序在 UNIX/LINUX 操作系统上运行。
有人可以向我解释为什么这里有多个 uvicorn 进程无法分叉一个新进程,以及为什么我会遇到这个胎面锁?
最后应该实现的目标很简单:
使用该 uvicorn 进程的 memory 副本生成多个其他进程(通过 fork 的子进程)的 uvicorn 进程。 执行 cpu 绑定任务。
TypeError:无法腌制 _thread.lock 对象
源于您传递到子流程中的任何数据
p = mp.Process(target=self.single_compute, args=single_comp_data)
包含不可腌制的 object。
所有发送到multiprocessing
子进程的 args/kwargs(无论是通过 Process,还是Pool
中的高级方法)都必须是可腌制的,同样,function 运行的返回值必须是可腌制的,以便可以将其发送回父进程.
如果您在 UNIX 上并使用fork
start 方法进行多处理(这是 Linux 上的默认设置,但不是在 macOS 上),您还可以利用写时复制 ZCD69B4957F06CD69B4957F06CD69B4957F06CD0818D7BFE3D61 语义来避免“复制”“复制”子进程通过使数据可用,例如通过实例 state,一个全局变量,...,在生成子进程之前,并让它通过引用获取它,而不是将数据本身作为参数传递下来。
此示例使用imap_unordered
来提高性能(假设不需要按顺序处理 id),并将返回一个 dict 将输入 ID 映射到它创建的结果。
class Compute:
_cache = {} # could be an instance variable too but whatever
def get_data(self, id):
if id not in self._cache:
self._cache[id] = get_data_from_somewhere(id)
return self._cache[id]
def compute_item(self, id):
data = self.get_data(id)
result = 42 # ... do heavy computation here ...
return (id, result)
def compute_result(self, ids) -> dict:
for id in ids:
self.get_data(id) # populate in parent process
with multiprocessing.Pool() as p:
return dict(p.imap_unordered(self.compute_item, ids))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.