Search code examples
python-asynciocoroutineevent-loop

Why loop.run_forever() is locking my main thread?


While learning asyncio I was trying this code:

import asyncio
from  asyncio.coroutines import coroutine

@coroutine
def coro():
    counter: int = 0
    while True:
        print("Executed" + str(counter))
        counter += 1
        yield


loop = asyncio.get_event_loop()
loop.run_until_complete(coro())
loop.run_forever()

print("Finished!")

I was expecting the coroutine to be executed only once because it contains a yield and should have returned control to the caller. The output I was expecting was:

Executed 0
Finished!

I was expecting this behaviour because I thought the loop was going to run the coroutine forever once every "frame" returning to the caller after each execution (something like a background thread but in a cooperative way). But instead, it runs the coroutine forever without returning?. Output is the following:

Executed 0
Executed 1
Executed 2
Executed 3
...

Could anyone explain why this happens instead of my expectations?

Cheers.


Solution

  • You have a couple of problems. When you call run_until_complete, it waits for coro to finish before moving on to your run_forever call. As you've defined it, coro never finishes. It contains an infinite loop that does nothing to break out of the loop. You need a break or a return somewhere inside the loop if you want to move on to the next step in your application.

    Once you've done that, though, your next call is to run_forever, which, just as its name suggests, will run forever. And in this case it won't have anything to do because you've scheduled nothing else with the event loop.

    I was expecting the coroutine to be executed only once because it contains a yield and should have returned control to the caller.

    Looking past the fact that your coroutine has no yield, awaiting (or yielding from depending on which syntax you choose to use) does not return control to the caller of run_until_complete or run_forever. It returns control to the event loop so that it can check for anything else that has been awaited and is ready to resume.