首页 > 解决方案 > `asyncio.wait([asyncio.sleep(5)])` 和 `asyncio.sleep(5)` 之间的区别

问题描述

coro2有人可以解释为什么完成和coro1完成之间有 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

标签: pythonpython-3.xpython-asynciocoroutine

解决方案


TLDR:不要time.sleep在协程中使用阻塞调用。用于asyncio.sleep异步暂停,如果必须运行阻塞代码,则使用事件循环执行器。


Usingasyncio.wait([thing])增加了一个间接级别,thing在一个新的 Future/Task 中执行。当 bareawait asyncio.sleep(5)在 期间执行 sleep 时coro1, Wrapped在所有其他当前调度的协程之后await asyncio.wait([asyncio.sleep(5)])执行 sleep 。

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- 和被恢复 - 包括 bare asyncio.sleep(5)。在前一种情况下,这意味着完成asyncio.sleep 后开始time.sleep- 因此需要10 + 5几秒钟才能完成。在后一种情况下,这意味着asyncio.sleep已经开始,它只是无法在 10 秒之前完成- 因此需要max(10, 5)几秒钟才能完成。


始终使用asyncio.sleep以获得所需的持续时间。如果必须执行阻塞代码,让它通过 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())

推荐阅读