FastAPI中run_in_threadpool运行缓慢卡顿问题求助
解决FastAPI中run_in_threadpool运行缓慢卡顿的问题
核心问题分析
你的卡顿问题主要来自两个关键点:
- 默认
run_in_threadpool使用的线程池无线程数限制,当list_of_required_entry规模较大时,会创建大量线程,引发频繁上下文切换,直接拖慢性能。 foo函数中pandas的merge操作在事件循环线程执行,属于CPU密集型任务,会阻塞事件循环,导致整个服务响应变慢。
解决方案
1. 自定义线程池限制线程数量
创建全局ThreadPoolExecutor并设置合理的max_workers(建议设为CPU核心数的1-2倍),替换Starlette默认线程池,让run_in_threadpool自动使用受限线程池。
from concurrent.futures import ThreadPoolExecutor import asyncio from fastapi import FastAPI import pandas as pd from functools import reduce import starlette.concurrency # 自定义线程池,限制线程数 thread_pool = ThreadPoolExecutor(max_workers=4) app = FastAPI() # 启动时替换默认线程池 @app.on_event("startup") async def startup(): starlette.concurrency.DEFAULT_THREAD_POOL = thread_pool # 关闭时清理线程池 @app.on_event("shutdown") def shutdown(): thread_pool.shutdown(wait=True)
2. 将所有CPU密集操作移至线程池
把bar调用和pandas的merge操作封装到线程函数中,避免在事件循环线程执行阻塞操作:
async def foo(param_1, param_2): def process_task(p1, p2): # 子线程中执行所有CPU密集操作 df_a, df_b = bar(p1, p2) return df_a.merge(df_b, on='name', how='left') # 使用自定义线程池执行任务 df = await asyncio.get_event_loop().run_in_executor(thread_pool, process_task, param_1, param_2) return df
3. 控制并发任务数(可选)
如果参数列表规模极大,可通过asyncio.Semaphore限制同时运行的任务数,避免瞬间压垮系统资源:
@app.get('/get_foo') async def get_price_comparison(): list_of_required_entry = [...] # 你的参数列表 tasks = [] # 限制同时运行的任务数 semaphore = asyncio.Semaphore(4) async def bounded_task(p1, p2): async with semaphore: return await foo(p1, p2) for param_1, param_2 in list_of_required_entry: tasks.append(asyncio.create_task(bounded_task(param_1, param_2))) data = await asyncio.gather(*tasks) data_dataframe = reduce(lambda left, right: pd.merge(left, right, on='name', how='left'), data) return data_dataframe.to_dict(orient='records')
关键注意事项
max_workers不要设置过大,过多线程会增加上下文切换开销,反而降低性能。- 所有pandas的CPU密集操作(如
merge、groupby)必须放到线程池执行,严禁阻塞事件循环。 - Semaphore的数值可根据硬件配置调整,平衡并发效率与资源占用。
内容的提问来源于stack exchange,提问作者r ram
相关产品推荐
相关产品推荐

