Python await 函数执行顺序的问题?

下面这段代码中,为何 ws.send_text("1") 必须等待 load_dataset("beans") 加载完毕才会执行呢?

不应该是先执行 ws.send_text("1") 然后再执行 load_dataset("beans") 吗?

load_dataset 是一个加载模型数据集的函数
from fastapi import FastAPI, WebSocket
from datasets import load_dataset


app = FastAPI()


@app.websocket("/")
async def h(ws: WebSocket):
    await ws.accept()
    await ws.send_text("1")
    dataset = load_dataset("beans")
    await ws.send_text("2")
阅读 1.3k
avatarAI BotBETA

在 Python 中,await 关键字用于挂起当前异步函数的执行,直到等待的异步操作完成。在你提供的代码中,await ws.send(1)await ws.send(2) 会等待它们各自的发送操作完成。然而,它们并不会阻塞 load_dataset("beans") 的执行,因为 load_dataset 函数也是异步的(假设它使用了 asyncawait)。

这意味着,await ws.send(1) 发送消息后,控制权会立即返回给事件循环,事件循环会检查是否有其他可以并行执行的异步任务。在这种情况下,load_dataset("beans") 可以开始执行,因为它也是一个异步函数。

所以,ws.send(1) 不需要等待 load_dataset("beans") 加载完毕才会执行。实际上,ws.send(1)load_dataset("beans") 可能会并行执行,这取决于事件循环如何调度这些异步任务。

如果你发现 ws.send(1) 必须等待 load_dataset("beans") 加载完毕才会执行,可能是因为 load_dataset 函数实际上是同步的,或者它内部有一些同步操作导致它不能并行执行。你可以检查 load_dataset 函数的实现,确保它是异步的,并且没有阻塞操作。

另外,需要注意的是,由于 ws.send(1)load_dataset("beans") 是并行执行的,所以它们之间的执行顺序并不是固定的。也就是说,有时候 ws.send(1) 可能先执行,有时候 load_dataset("beans") 可能先执行,这取决于事件循环的调度和当前系统的负载情况。

如果你需要确保 ws.send(1)load_dataset("beans") 之前执行,你可以使用 await 来等待 ws.send(1) 的发送操作完成,然后再执行 load_dataset("beans")。但是,这仍然不能保证 ws.send(1)load_dataset("beans") 之前完成,因为这两个操作是并行执行的。如果你需要严格的执行顺序,你可能需要重新考虑你的代码结构,确保按照你想要的顺序执行操作。

1 个回答

很疑惑啊。。。我把我的代码和版本什么的都贴出来,还有我的试验结果。。。我的结论还是第一个await 是正常 send 出去了的。

  1. 先贴结果
  • 浏览器的结果
    image.png
  • 服务端的日志
    image.png
  1. 我的依赖环境
fastapi = "^0.110.0"
uvicorn = {extras = ["standard"], version = "^0.28.0"}
websockets = "^12.0"
datasets = "^2.18.0"
  1. 我的示例代码

为了能明确看出前后顺序差异,我加入了时间戳 datetime.now() 在每个信息内容上。

from datetime import datetime

from datasets import load_dataset
from fastapi import FastAPI, WebSocket
from fastapi.responses import HTMLResponse


app = FastAPI()


html = """
<!DOCTYPE html>
<html>
    <head>
        <title>Chat</title>
    </head>
    <body>
        <h1>WebSocket Chat</h1>
        <form action="" onsubmit="sendMessage(event)">
            <input type="text" id="messageText" autocomplete="off"/>
            <button>Send</button>
        </form>
        <ul id='messages'>
        </ul>
        <script>
            var ws = new WebSocket("ws://localhost:8081/ws");
            ws.onmessage = function(event) {
                var messages = document.getElementById('messages')
                var message = document.createElement('li')
                var content = document.createTextNode(event.data)
                message.appendChild(content)
                messages.appendChild(message)
            };
            function sendMessage(event) {
                var input = document.getElementById("messageText")
                ws.send(input.value)
                input.value = ''
                event.preventDefault()
            }
        </script>
    </body>
</html>
"""


@app.get("/")
async def get():
    return HTMLResponse(html)


@app.websocket("/ws")
async def h(ws: WebSocket):
    await ws.accept()
    await ws.send_text(f"1: {datetime.now()}")
    dataset = load_dataset("beans")
    print(f"time: {datetime.now()} => dataset: {dataset}")
    await ws.send_text(f"2: {datetime.now()}")

    while True:
        data = await ws.receive_text()
        await ws.send_text(f"Message text was: {data}, datetime: {datetime.now()}")

从我上面截图和我的实际操作来看,1 确实是先执行的,因为 html 接收到了 ws 的信息。
你可以拿我的去你那里在试验一下。。。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题