Difference between `asyncio.wait([asyncio.sleep(5)])` and `asyncio.sleep(5)`

非 Y 不嫁゛ 提交于 2021-02-15 07:33:49

问题


Could somebody please explain why there is a 5 second delay between coro2 finishing and coro1 finishing?

Also, why is there no such delay if I replace asyncio.wait([asyncio.sleep(5)]) with 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

回答1:


TLDR: Do not use blocking calls such as time.sleep in a coroutine. Use asyncio.sleep to asynchronously pause, or use an event loop executor if blocking code must be run.


Using asyncio.wait([thing]) adds a level of indirection, executing thing in a new Future/Task. While a bare await asyncio.sleep(5) executes the sleep during coro1, the wrapped await asyncio.wait([asyncio.sleep(5)]) executes the sleep after all other currently scheduled coroutines.

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")

Since coro2 uses the blocking time.sleep(10), it disables the event loop and all other coroutines.

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

This prevents further Futures from being started - including new future from asyncio.wait - and from being resumed - including the bare asyncio.sleep(5). In the former case, that means the asyncio.sleep starts after the time.sleep is done - therefore taking 10 + 5 seconds to complete. In the latter case, that means the asyncio.sleep has already started, it just cannot complete before the 10 seconds are up - therefore taking max(10, 5) seconds to complete.


Consistently use asyncio.sleep to get the desired durations. If blocking code must be executed, have it 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())


来源:https://stackoverflow.com/questions/62001898/difference-between-asyncio-waitasyncio-sleep5-and-asyncio-sleep5

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!