事例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博客