为什么在这个示例代码中需要使用run_forever?

5
在Python的asyncio websockets库中,示例调用了run_forever()。为什么需要这样做? run_until_complete()不应该阻塞并运行websockets循环吗?
#!/usr/bin/env python

# WS server example

import asyncio
import websockets

async def hello(websocket, path):
    name = await websocket.recv()
    print(f"< {name}")

    greeting = f"Hello {name}!"

    await websocket.send(greeting)
    print(f"> {greeting}")

start_server = websockets.serve(hello, "localhost", 8765)

asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
# if you comment out the above line, this doesn't work, i.e., the server
# doesn't actually block waiting for data...

如果我注释掉run_forever(),程序会立即结束。
start_server是库返回的可等待对象。为什么run_until_complete不能足够地使其阻塞/等待hello()呢?
1个回答

3

websockets.serve只是简单地启动服务器并立即退出。(仍需要等待其完成,因为配置服务器可能需要网络通信。)由于这一点,您需要实际运行事件循环。

由于服务器旨在无限期运行,因此您不能通过将协程传递给run_until_complete的常规方式来运行事件循环。由于服务器已经启动,因此没有要运行的协程,您只需要让事件循环运行并执行其任务。这就是run_forever有用的地方-它告诉事件循环无限期地运行(执行先前安排的任务,例如属于服务器的任务),或者直到通过调用loop.stop停止。

在Python 3.7及更高版本中,应使用asyncio.run来运行asyncio代码,它将创建一个新的事件循环,因此上述技巧无法使用。在现代asyncio代码中实现上述目标的好方法是使用serve_forever方法(未经测试):

async def my_server():
    ws_server = await websockets.serve(hello, "localhost", 8765)
    await ws_server.server.serve_forever()

asyncio.run(my_server())

谢谢 - 我现在意识到协议需要事件循环才能做任何有用的事情。这很令人困惑:如果您不保持循环活动,协议就像幽灵任务一样悄然死去,但它们不会出现在asyncio.all_tasks()中。希望有一个方法asyncio.show_why_i_shouldnt_close_this_loop()返回一组工作协议。Autobahn的人也使用该模式transport, protocol = loop.run_until_complete(coroutine)

然后...

loop.run_forever()
- Anthony Alba
@AnthonyAlba 事件循环必须存在于任何与asyncio相关的工作中,不仅仅是协议和任务。协议不是幽灵任务,它们是一种不同的高级抽象,使用回调函数来实现其魔力。这些回调函数是使用基本的事件循环方法安装的,例如add_readeradd_writercall_latercall_soon等等,而正是这些方法需要事件循环实际运行才能保持轮子转动。 - user4815162342

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接