Event loop is not stopped after calling stop() method using python asyncio module
Question:
I have written the following code:
import asyncio
import time
from threading import Thread
from threading import Timer
async def myCouroutine():
time.sleep(3)
dbAccessLoop = None
def stopEventLoop():
dbAccessLoop.stop()
def eventLoopThreadMain():
global dbAccessLoop
try:
dbAccessLoop = asyncio.new_event_loop()
dbAccessLoop.run_forever()
finally:
dbAccessLoop.close()
def main():
thread = Thread(target = eventLoopThreadMain)
thread.start()
stopLoopTimer = Timer(3, stopEventLoop)
stopLoopTimer.start()
while dbAccessLoop is not None and dbAccessLoop.is_running():
dbAccessLoop.call_soon_threadsafe(asyncio.ensure_future, myCouroutine())
break
thread.join()
if __name__ == '__main__':
main()
The program creates new event loop, starts it, runs coroutine (doing some work) and then after some time using timer it stops the loop. I expect that after calling stop() method dbAccessLoop.run_forever() will give execution back, but that is not happening.
Do I missed something here? My guess is that stop() method is not thread safe but I do not see any errors in the console. Thanks in advance.
Answers:
There seems to be a hole at least in the setting of the dbAccessLoop
.
main -> thread: start
main -> while: dbAccessLoop is None => bailout
thread -> dbAccessLoop = whatever
Apart from that, if thread
gets to set dbAccessLoop
, it will spawn a tremendous amount of async jobs in the loop, each one waiting to be finished.
It is very well possible that the job queue is just so full of 3-second waits that the stop
, even if it were thread safe, only gets executed after 3155123412 seconds.
Also, https://docs.python.org/3/library/asyncio-dev.html#detect-never-awaited-coroutines tells me that the coroutine myCoroutine
might not be scheduled. But that’s another problem.
And apart from that, indeed "Almost all asyncio objects are not thread safe, which is typically not a problem unless there is code that works with them from outside of a Task or a callback." (https://docs.python.org/3/library/asyncio-dev.html#asyncio-multithreading), so the code should call the stop
in the eventloop’s thread using dbAccessLoop.call_soon_threadsafe(dbAccessLoop.stop)
.
I have written the following code:
import asyncio
import time
from threading import Thread
from threading import Timer
async def myCouroutine():
time.sleep(3)
dbAccessLoop = None
def stopEventLoop():
dbAccessLoop.stop()
def eventLoopThreadMain():
global dbAccessLoop
try:
dbAccessLoop = asyncio.new_event_loop()
dbAccessLoop.run_forever()
finally:
dbAccessLoop.close()
def main():
thread = Thread(target = eventLoopThreadMain)
thread.start()
stopLoopTimer = Timer(3, stopEventLoop)
stopLoopTimer.start()
while dbAccessLoop is not None and dbAccessLoop.is_running():
dbAccessLoop.call_soon_threadsafe(asyncio.ensure_future, myCouroutine())
break
thread.join()
if __name__ == '__main__':
main()
The program creates new event loop, starts it, runs coroutine (doing some work) and then after some time using timer it stops the loop. I expect that after calling stop() method dbAccessLoop.run_forever() will give execution back, but that is not happening.
Do I missed something here? My guess is that stop() method is not thread safe but I do not see any errors in the console. Thanks in advance.
There seems to be a hole at least in the setting of the dbAccessLoop
.
main -> thread: start
main -> while: dbAccessLoop is None => bailout
thread -> dbAccessLoop = whatever
Apart from that, if thread
gets to set dbAccessLoop
, it will spawn a tremendous amount of async jobs in the loop, each one waiting to be finished.
It is very well possible that the job queue is just so full of 3-second waits that the stop
, even if it were thread safe, only gets executed after 3155123412 seconds.
Also, https://docs.python.org/3/library/asyncio-dev.html#detect-never-awaited-coroutines tells me that the coroutine myCoroutine
might not be scheduled. But that’s another problem.
And apart from that, indeed "Almost all asyncio objects are not thread safe, which is typically not a problem unless there is code that works with them from outside of a Task or a callback." (https://docs.python.org/3/library/asyncio-dev.html#asyncio-multithreading), so the code should call the stop
in the eventloop’s thread using dbAccessLoop.call_soon_threadsafe(dbAccessLoop.stop)
.