fastapi異步編程與await關(guān)鍵字:ws.send_text()和load_dataset()的執(zhí)行順序
本文探討在FastAPI框架中使用async/await進行異步編程時,ws.send_text()和load_dataset()函數(shù)的執(zhí)行順序問題。 之前的代碼示例中,存在一個誤解:ws.send_text(“1”)似乎需要等待load_dataset(“beans”)完成才能執(zhí)行。實際上并非如此。
關(guān)鍵在于理解await關(guān)鍵字的作用和load_dataset()函數(shù)的特性。 await 僅用于等待異步操作完成。ws.send_text()是一個異步操作,因此await ws.send_text(“1”)會等待消息發(fā)送完成。然而,load_dataset(“beans”)是一個同步阻塞操作,它會阻塞當(dāng)前協(xié)程直到數(shù)據(jù)集加載完成。
代碼執(zhí)行流程分析:
- await ws.accept(): 等待websocket連接建立。
- await ws.send_text(“1”): 異步發(fā)送消息”1″,此操作完成后繼續(xù)執(zhí)行。
- dataset = load_dataset(“beans”): 同步阻塞操作開始,程序在此處暫停,直到load_dataset(“beans”)從遠程下載并加載數(shù)據(jù)集完成。
- await ws.send_text(“2”): 異步發(fā)送消息”2″,同樣需要等待發(fā)送完成。
實驗驗證與結(jié)果解讀:
實驗結(jié)果清晰地表明,瀏覽器端先接收到”1″,然后才是”2″, 這與load_dataset(“beans”)的阻塞特性相符。 雖然ws.send_text(“1”)先執(zhí)行,但load_dataset(“beans”)的阻塞導(dǎo)致”2″的發(fā)送被延遲到數(shù)據(jù)集加載完成之后。
改進代碼以實現(xiàn)并發(fā):
如果需要load_dataset(“beans”)與ws.send_text(“1”)并發(fā)執(zhí)行,需要將load_dataset(“beans”)改造成異步操作。這通常需要使用異步IO庫,例如aiohttp來下載數(shù)據(jù)。 以下是一個改進后的示例(假設(shè)已使用aiohttp并實現(xiàn)了一個異步的load_dataset_async函數(shù)):
import asyncio from datetime import datetime from datasets import load_dataset from fastapi import FastAPI, WebSocket from fastapi.responses import HTMLResponse app = FastAPI() # ... (HTML code remains the same) ... @app.websocket("/ws") async def h(ws: WebSocket): await ws.accept() task = asyncio.create_task(load_dataset_async("beans")) # Asynchronously load dataset await ws.send_text(f"1: {datetime.now()}") dataset = await task # Await the dataset loading task print(f"time: {datetime.now()} => dataset: {dataset}") await ws.send_text(f"2: {datetime.now()}") # ... (rest of the code remains the same) ...
通過asyncio.create_task()創(chuàng)建異步任務(wù),load_dataset_async(“beans”)可以在后臺并發(fā)執(zhí)行,而不會阻塞主協(xié)程。
總結(jié): 原代碼中ws.send_text(“1”)先執(zhí)行,但load_dataset(“beans”)的同步阻塞特性決定了后續(xù)操作的執(zhí)行順序。 要實現(xiàn)并發(fā),必須將數(shù)據(jù)加載操作異步化。