Issue
There is one (or more - doesn't matter) coroutine that does nothing but sleeps for 0.1 seconds. Main() just creates a task for the coroutine, starts it (by await sleep(0)) and sleeps itself for o.5 seconds (any time more that coroutine).
- Q1: Why after Main() wakes up the coroutine is not finished yet?
- Q2: Why I need exactly 3x await to finish coroutine? (tried with more numbers of coroutines - still 3 times)
Code:
import asyncio
import time
async def coro():
print(' coro started to sleep for 0.1 second')
await asyncio.sleep(0.1)
print(' [x] coro finished')
async def main():
coro_task = asyncio.create_task(coro())
await asyncio.sleep(0)
time.sleep(0.5)
i = 0
while not coro_task.done():
i += 1
print(i)
await asyncio.sleep(0)
asyncio.run(main())
The output:
coro started to sleep for 0.1 second
1
2
3
[x] coro finished
If I put 0.1 to time.sleep() there will be thousands of cycles before the coroutine is done. The more number I put the less cycles I have. Once I put anything more than the coroutine sleep, it always become 3.
Solution
It is due to the way the asyncio loop implementation works internally. The exact reasoning of why it needs 3 cycles until the task is marked is done, can only be tracked by debugging the asyncio loop code itself - but it is safe to think of it as an implementation detail.
Once you dig in that code , you will see that the event loop will run its internal _run_once
function, which executes tasks in a careful order, and do each stage one time. Certainly, following there, you will find out that once the task is finished, the stage that ultimately will mark the task as done, will only be executed on the next pass of the loop.
It also surprises me - even with the above, the common sense is your code should count to "2". By debugging it, one will certainly find out an intermediate step there, which is executed in the second pass.
If for anything, your code works as a fantastic event of why it is needed to yield to the asyncio loop several times to keep things running, even for things that we think should be complete in a single step.
Ah, I debugged it a little and found out why the extra pass: one pass is needed to actually finish the asyncio.sleep
called inside the task in the code,as described above.
Answered By - jsbueno
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.