命令行运行的Django脚本如何实现请求并行执行?
问题根因
协程代码没有实现并行的核心原因是:requests是同步阻塞HTTP库,在asyncio事件循环中直接调用阻塞IO操作时,会完全占住事件循环的执行权,直到当前请求返回结果之前,事件循环根本没有机会调度其他协程运行。因此哪怕你用asyncio.create_task创建了多个任务,也只能等上一个任务的阻塞请求完全执行完,才会开始跑下一个任务,最终表现为串行执行。
可行解决方案
方案1:使用原生异步HTTP客户端(推荐)
将同步的requests替换为适配asyncio的异步HTTP库(比如aiohttp),这类库在等待IO响应时会主动让出事件循环的执行权,让其他协程可以被调度执行,真正实现异步并发。
修改后的完整可运行代码如下:
import asyncio import aiohttp from asgiref.sync import async_to_sync async def do_task(task, session): print(f"starting task {task}") async with session.get("https://swapi.dev/api/people/1") as response: response_json = await response.json() print(response_json) print(f"finished task {task}") async def run(): # 复用同一个客户端会话,减少连接开销 async with aiohttp.ClientSession() as session: tasks = set() for i in range(5): task = asyncio.create_task(do_task(i, session)) tasks.add(task) task.add_done_callback(tasks.discard) await asyncio.gather(*tasks) if __name__ in ("__main__", "django.core.management.commands.shell"): print("==start==") async_to_sync(run)() print("==done==")
方案2:将阻塞请求放到线程池执行
如果不想替换现有requests相关逻辑,可以用asyncio.to_thread把阻塞的请求操作放到独立线程中运行,避免阻塞主事件循环,也能实现并发效果。
只需要修改do_task函数即可:
import requests import asyncio async def do_task(task): print(f"starting task {task}") # 把阻塞调用交给线程池执行,不占用事件循环 response = await asyncio.to_thread(requests.get, "https://swapi.dev/api/people/1") response_json = response.json() print(response_json) print(f"finished task {task}")
注意:该方案依赖线程切换实现并发,性能弱于原生异步实现,仅适合不想大规模修改原有同步代码的临时场景。
运行效果
修改完成后执行脚本,就会先批量打印所有任务的启动日志,再陆续返回接口响应、打印任务完成日志,和预期的输出形式一致。
内容的提问来源于stack exchange,提问作者Android
相关产品推荐
相关产品推荐

