如何在Django 3中使用内置async、asyncio与Uvicorn实现模型异步计算及存储?
用Python async/asyncio + Uvicorn改造Django 3的异步模型操作
首先得明确:Django 3.x的ORM完全不支持异步操作,这是你之前改造碰壁的核心原因之一——直接在异步函数里调用同步ORM方法会阻塞事件循环,甚至引发错误。下面是用Python内置工具实现需求的可行方案,核心思路是「异步调度+线程池执行同步ORM操作」:
步骤1:改造信号处理流程
Django 3不支持异步信号处理器,所以我们需要保留一个同步的信号回调函数,在里面启动异步任务;真正的耗时逻辑放到异步函数中,同步ORM操作则委托给线程池执行。
修改后的models.py代码
import asyncio import logging from concurrent.futures import ThreadPoolExecutor from django.db import transaction from django.db.models.signals import post_save from django.dispatch import receiver from .models import Case, ModelImages # 初始化线程池,用于执行同步ORM操作,大小根据服务器配置调整 executor = ThreadPoolExecutor(max_workers=4) logger = logging.getLogger(__name__) def get_slices(instance): # 你的原有切片生成逻辑 # 如果这部分是CPU密集型,建议也放到线程池执行(见下文说明) return images_array_list # 同步ORM操作的封装函数,必须在线程池中执行 def _sync_save_model_image(instance, img_name): with transaction.atomic(): case_img = ModelImages(case=instance, img=img_name) case_img.save() # 异步保存单张图片的函数 async def async_save_model_image(instance, img_name): loop = asyncio.get_event_loop() try: # 把同步保存操作提交给线程池执行 await loop.run_in_executor(executor, _sync_save_model_image, instance, img_name) except Exception as e: logger.error(f"Failed to save image {img_name}: {str(e)}") # 异步处理3D图片的核心函数 async def process_3d_image_async(instance): # 处理切片生成: # 如果get_slices是IO密集型(比如读文件/调用外部API),建议改写成async函数并await # 如果是CPU密集型,同样用线程池执行: # loop = asyncio.get_event_loop() # imgs = await loop.run_in_executor(executor, get_slices, instance) imgs = get_slices(instance) # 批量异步保存所有图片 save_tasks = [async_save_model_image(instance, img) for img in imgs] await asyncio.gather(*save_tasks) # 同步信号回调,负责启动异步任务 @receiver(post_save, sender=Case) def process_3d_image(sender, instance, created, **kwargs): if created: # 启动异步任务,依赖活跃的asyncio事件循环(Uvicorn提供) asyncio.create_task(process_3d_image_async(instance))
步骤2:用Uvicorn作为ASGI服务器运行项目
Django默认的runserver是WSGI服务器,没有活跃的asyncio事件循环,asyncio.create_task会报错。必须用Uvicorn启动项目:
uvicorn your_project_name.asgi:application --reload
关键注意事项
- ORM操作必须放线程池:Django 3.x的ORM是同步实现,不能在异步协程中直接调用,否则会阻塞整个事件循环,甚至触发数据库连接错误。
- 根据任务类型优化get_slices:
- 如果是IO密集型(比如读取本地3D文件、调用第三方图片处理API),建议用
aiofiles等异步库重写成async函数,避免阻塞。 - 如果是CPU密集型(比如大量图像计算),一定要放到线程池(或进程池)执行,否则会占用事件循环线程,导致整个服务响应变慢。
- 如果是IO密集型(比如读取本地3D文件、调用第三方图片处理API),建议用
- 错误处理与日志:异步任务的异常不会自动抛出到主线程,所以一定要在异步函数中添加try-except块,用日志记录错误,方便排查问题。
- 线程池大小调整:根据服务器的CPU核心数和并发量调整
max_workers,避免创建过多线程导致资源耗尽。
为什么Celery/Trio可能没生效?
- Celery需要正确配置消息队列(比如Redis/RabbitMQ),如果配置不当或者任务没有被正确消费,就会看起来没效果。
- Trio是独立的异步框架,和Django的WSGI环境兼容性差,必须配合ASGI服务器才能正常运行,而且同样需要处理ORM的同步问题。
内容的提问来源于stack exchange,提问作者Pezz
相关产品推荐
相关产品推荐

