python - 未等待的任务总是在通过 create_task 创建时开始吗?

标签 python python-3.x async-await

我正在尝试创建服务器,但我很难理解如何使用 create_task 启动协程。在第一个测试中,create_task 似乎立即启动任务。但是在第二个测试中,它似乎直到等待它才开始。

import asyncio

async def task_test():
    async def delayed_print(delay, message):
        await asyncio.sleep(delay)
        print(message)

    print_task = asyncio.create_task(delayed_print(2, "Hello"))
    await asyncio.sleep(5)
    print("World")
    await print_task

asyncio.run(task_test())
Hello
World

如果 print_task 仅在到达 await print_task 时才开始,则将打印“World\nHello”。


问题是,这似乎与我在 AbstractServerserve_forever 函数中看到的行为相矛盾。如果我为启动服务器设置类似的测试:

async def server_test():
    server: asyncio.AbstractServer = await asyncio.start_server(lambda r, w: print("conn"), "127.0.0.1", 5555)

    serve_task = asyncio.create_task(server.serve_forever())

    # await serve_task  # The pivotal part
    return server

服务器仅在(当前注释的)await 行执行时接受传入连接;建议 serve_forever 需要 awaiting 才能正常工作。

证据:

asyncio.run(server_test())  # With "await serve_task" commented out
# Returns

# --- In another REPL

rdr, wtr = asyncio.run(asyncio.open_connection("127.0.0.1", 5555))
Traceback (most recent call last):
    # Truncated - It's very long
    raise OSError(err, f'Connect call failed {address}')
ConnectionRefusedError: [Errno 10061] Connect call failed ('127.0.0.1', 5555)

由于客户端无法连接到服务器而出错。

如果我取消注释该行:

asyncio.run(server_test())  # With "await serve_task" executing
# Never returns

# --- In another REPL

rdr, wtr = asyncio.run(asyncio.open_connection("127.0.0.1", 5555))

(rdr, wtr)
(<StreamReader transport=<_SelectorSocketTransport fd=1064>>, <StreamWriter transport=<_SelectorSocketTransport fd=1064> reader=<StreamReader transport=<_SelectorSocketTransport fd=1064>>>)

它连接成功(“conn”在服务器 REPL 中打印出来)。

谁能解释为什么 serve_forever 只允许服务器在等待时接受连接?我宁愿不需要显式地 await serve_task。为什么?:

  • 我想了解为什么这两个非常相似的代码之间存在差异,这样我就可以避免 future 的陷阱,但主要是......

  • 因为我不想await serve_forever。这将创建一个有效的无限阻塞调用,阻止服务器执行任何新操作。理想情况下,我希望能够在 REPL 中启动服务器,并向本地服务器发送命令以执行操作。按照现在的情况,只要我启动服务器,REPL 就会被阻塞。我想出的唯一解决方法是预先创建我要运行的任务并延迟它们,然后将它们和 serve_forever 交给 gather。像这样的东西:

    asyncio.gather(server.serve_forever(),
                   some_delayed_task,
                   some_other_delayed_task)
    

此处的任何清晰度将不胜感激。

最佳答案

您不必等待 create_task(serve_forever()) 的结果。但是,您确实需要await something。在asyncio中,一次只能发生一件事,并且只能通过await在任务之间切换。在您到达 await 之前,serve_forever 任务实际上并未运行。

您在 REPL 中看到的问题是因为 REPL 不是根据 asyncio 实现的,所以当您坐在 repl 提示符下时没有 asyncio 可以运行任务。尝试使用 aioconsole而不是标准的 REPL。

关于python - 未等待的任务总是在通过 create_task 创建时开始吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57734313/

相关文章:

c# - 如何编写带有 out 参数的异步方法?

Python:如何为单个迹线添加辅助 x 轴?

python - Alembic SqlAlchemy Postgres "NameError: name ' 字符串'未定义“试图添加数组(字符串)字段

python - 按类别计算精度和召回率

python-3.x - 将 Pandas 数据框转换为包含 ID 和权重的元组列表

javascript - 访问从异步函数返回的 promise 实例

c# - 在 C# .NET block 中等待 Task.WhenAll 吗?

Python/Pandas 从 DataFrames 中过滤掉唯一的行

python - Python中基于多个复杂条件的Join

python - 在 Python 3.4 上设置 newRelic 的问题