【问题标题】:Difference between `asyncio.wait([asyncio.sleep(5)])` and `asyncio.sleep(5)``asyncio.wait([asyncio.sleep(5)])` 和 `asyncio.sleep(5)` 之间的区别
【发布时间】:2020-09-12 01:28:08
【问题描述】:

有人能解释一下为什么coro2finishing 和coro1finishing 之间有 5 秒的延迟吗?

另外,如果我将asyncio.wait([asyncio.sleep(5)]) 替换为asyncio.sleep(5),为什么没有这样的延迟?

async def coro1():
    logger.info("coro1 start")
    await asyncio.wait([asyncio.sleep(5)])
    logger.info("coro1 finish")

async def coro2():
    logger.info("coro2 start")
    time.sleep(10)
    logger.info("coro2 finish")

async def main():
    await asyncio.gather(coro1(), coro2())

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
2020-05-25 12:44:56 coro1 start
2020-05-25 12:44:56 coro2 start
2020-05-25 12:45:06 coro2 finish
2020-05-25 12:45:11 coro1 finish

【问题讨论】:

  • 不要在协程中使用time.sleep(10)。它阻塞了整个事件循环和所有其他协程。

标签: python python-3.x python-asyncio coroutine


【解决方案1】:

TLDR:不要在协程中使用阻塞调用,例如time.sleep。使用asyncio.sleep 异步暂停,如果必须运行阻塞代码,则使用an event loop executor


使用asyncio.wait([thing]) 会增加一个间接级别,在新的Future/Task 中执行thing。虽然裸 await asyncio.sleep(5)coro1 期间执行睡眠,但包装的 await asyncio.wait([asyncio.sleep(5)]) 执行睡眠毕竟其他当前调度的协程。

async def coro1():
    logger.info("coro1 start")
    await asyncio.sleep(5)   # started immediately
    logger.info("coro1 finish")

async def coro1():
    logger.info("coro1 start")
    await asyncio.wait([     # started immediately
        asyncio.sleep(5)     # started in new task
    ])
    logger.info("coro1 finish")

由于coro2 使用阻塞time.sleep(10),它会禁用事件循环和所有其他协程。

async def coro2():
    logger.info("coro2 start")
    time.sleep(10)           # nothing happens for 10 seconds
    logger.info("coro2 finish")

这会阻止更多的 Futures 被启动 - 包括来自 asyncio.wait 的新未来 - 以及被恢复 - 包括裸露的 asyncio.sleep(5)。在前一种情况下,这意味着 asyncio.sleep time.sleep 完成之后开始 - 因此需要 10 + 5 秒才能完成。在后一种情况下,这意味着 asyncio.sleep 已经开始,它只是无法在 10 秒之前完成 - 因此需要 max(10, 5) 秒才能完成。


始终使用asyncio.sleep 来获得所需的持续时间。如果必须执行阻塞代码,请使用run via an executor

async def coro1w():
    print("coro1w start", time.asctime())
    await asyncio.wait([asyncio.sleep(5)])
    print("coro1w finish", time.asctime())

async def coro1b():
    print("coro1b start", time.asctime())
    await asyncio.sleep(5)
    print("coro1b finish", time.asctime())

async def coro2a():
    print("coro2a start", time.asctime())
    await asyncio.sleep(10)            # asynchronous sleep
    print("coro2a finish", time.asctime())

async def coro2t():
    print("coro2t start", time.asctime())
    loop = asyncio.get_running_loop()  # threaded sleep
    await loop.run_in_executor(None, lambda: time.sleep(10))
    print("coro2t finish", time.asctime())

async def main():
    await asyncio.gather(coro1w(), coro1b(), coro2a(), coro2t())

asyncio.run(main())

【讨论】:

  • 非常感谢您的回答。我知道你不应该使用time.sleep,而是使用asyncio.sleep。我用它来模拟不可避免的阻塞代码。在使用time.sleep的同时有什么方法可以避免延迟?
  • 所有 Python 异步框架都允许在线程中运行阻塞代码。对于 asycio,请使用 asyncio.loop.run_in_executor
猜你喜欢
  • 1970-01-01
  • 2019-11-05
  • 1970-01-01
  • 2018-11-16
  • 2019-11-17
  • 2010-12-28
  • 1970-01-01
相关资源
最近更新 更多