事例1:
@app.post("/predictfunc")
async def predictfunc(item: Item):# 使用asyncio.to_thread()在单独的线程中运行predict_in_threadresult = await asyncio.to_thread(predictfunc_main, item)return result
事例2:
@app.post("/remove_watermark_folder/")
async def remove_watermark_folder(folder_paths: FolderPaths):Path(folder_paths.output).mkdir(parents=True, exist_ok=True)tasks = []for filename in Path(folder_paths.input).iterdir():if filename.is_file() and filename.suffix.lower() in ['.jpg', '.png', '.jpeg']:input_path = filenamepic_name=Path(input_path).nameif len(str(input_path))>0:task = asyncio.create_task(remove_watermark(input_path, folder_paths.output+pic_name))tasks.append(task)await asyncio.gather(*tasks)return {"message": "Watermarks removed and images saved successfully."}
问题:
这两种异步有什么区别
事例1:使用asyncio.to_thread
目的:
- 当你的异步函数中需要调用一个阻塞的、不支持异步的同步函数时(比如某些需要CPU密集型计算的任务或库不支持异步IO的情况),
asyncio.to_thread
是一个非常有用的工具。它允许你将这个同步函数在单独的线程中运行,而不会阻塞主事件循环。
特点:
predictfunc
函数接收一个Item
对象作为参数,并使用asyncio.to_thread
来调用predictfunc_main
函数,这个函数可能是同步的且计算量较大。- 使用
await
等待asyncio.to_thread
的结果,这意味着主函数会暂停执行,直到predictfunc_main
函数在另一个线程中完成执行。
事例2:使用asyncio.create_task
和asyncio.gather
目的:
- 当你的异步函数中需要并行处理多个异步任务时,可以使用
asyncio.create_task
来创建任务,并使用asyncio.gather
来等待所有任务完成。这特别适用于IO密集型任务,如文件读写、网络请求等。
特点:
remove_watermark_folder
函数遍历一个文件夹中的文件,对符合条件的图片文件(如jpg、png、jpeg格式)并行执行去水印操作。- 对于每个符合条件的文件,使用
asyncio.create_task
创建一个去水印的任务(假设remove_watermark
是一个异步函数),并将这些任务添加到tasks
列表中。 - 使用
await asyncio.gather(*tasks)
等待所有去水印任务完成。asyncio.gather
会返回一个包含所有任务结果的元组,但在这个例子中,我们并没有直接使用这个结果,而是直接返回了一个表示成功的消息。
总结
- 事例1适用于需要在异步函数中调用同步函数,且该函数执行时间较长或计算量较大的情况。
- 事例2适用于需要并行处理多个异步任务,特别是IO密集型任务的情况。
两种写法都利用了Python的异步编程特性来提高程序的效率和响应性,但适用的场景和解决的问题不同。
线程池方式实现异步:
在fastapi中实现异步_fastapi 异步-CSDN博客