Issue
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
Solution
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())
Answered By - MisterMiyagi
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.