FastAPI中asyncio.create_task未后台运行异步方法的问题
问题原因
你的代码里update_materialized_view是同步阻塞函数,里面的数据库操作(调用存储过程、查询视图更新时间)都是阻塞式的。在异步函数_semaphore_update_mview_rasa_family_list中直接调用这个同步函数时,会占用FastAPI的异步事件循环线程,导致整个事件循环被卡住——即使你用loop.create_task把任务丢到后台,事件循环也得等这个同步任务执行完才能处理接口的返回逻辑,所以接口无法立即返回transaction_id。
解决方法
核心思路是把同步阻塞的数据库操作从事件循环线程中剥离,避免阻塞事件循环。最简便的方案是用asyncio.to_thread将同步函数放到线程池执行,这样事件循环可以继续处理其他请求(包括当前接口的返回)。
修改后的代码示例
import asyncio from uuid import uuid4 from fastapi import FastAPI # 假设其他依赖(get_db_session、log_file_and_shelve等)已正确导入 semaphore_update_mview_rasa_family_list = asyncio.Semaphore(1) app = FastAPI() def update_materialized_view(id_=str(uuid4())): log_file_and_shelve(id_=id_, message="update_materialized_view started updating mview") session, engine = get_db_session() session.execute(text("CALL dbms_mview.refresh(:mview)"), params={"mview": 'MVIEW_RASA_FAMILY_LIST'}) session.close() q = last_mat_view_update_time() log_file_and_shelve(id_=id_, message=f"update_materialized_view finished updating mview {q}") return q def last_mat_view_update_time(): session, engine = get_db_session() q = session.execute(text(""" SELECT to_char(LAST_REFRESH_DATE, 'yyyymmdd hh24:mi:ss', 'nls_calendar=persian') j_LAST_REFRESH_DATE, to_char(LAST_REFRESH_END_TIME, 'yyyymmdd hh24:mi:ss', 'nls_calendar=persian') j_LAST_REFRESH_END_TIME, LAST_REFRESH_DATE, LAST_REFRESH_END_TIME FROM USER_MVIEWS WHERE MVIEW_NAME = 'MVIEW_RASA_FAMILY_LIST' """)).fetchone() session.close() return list(q) async def _semaphore_update_mview_rasa_family_list(id_=str(uuid4())): async with semaphore_update_mview_rasa_family_list: # 用to_thread把同步函数丢到线程池执行,不阻塞事件循环 await asyncio.to_thread(update_materialized_view, id_=id_) await asyncio.sleep(1) @app.get("/update-mat-view", tags=['Database Service', 'Update Database']) async def update_mview_rasa_family_list(): id_ = str(uuid4()) asyncio.create_task(_semaphore_update_mview_rasa_family_list(id_=id_)) # 直接用asyncio.create_task即可,无需手动获取事件循环 return {"transaction_id": id_}
关键改动说明
- 把
update_materialized_view(id_=id_)替换为await asyncio.to_thread(update_materialized_view, id_=id_):to_thread会将同步函数提交到线程池执行,异步等待结果,期间事件循环可以正常处理其他任务(比如当前接口的返回)。 - 简化任务创建:直接用
asyncio.create_task即可,FastAPI的异步环境下会自动使用当前事件循环,无需手动获取。
进阶优化(可选)
如果希望彻底异步化,可以改用异步数据库驱动(比如Oracle的oracledb异步模式、PostgreSQL的asyncpg),将get_db_session、session.execute等操作改成异步方法,整个流程会完全非阻塞,性能更优,但需要调整数据库操作的相关代码。
内容的提问来源于stack exchange,提问作者Saeb Molaee
相关产品推荐
相关产品推荐

