I've got a simple test where I run a Python asyncio event loop using the run_forever
method, and then immediately stop it in another thread. However, the event loop does not seem to terminate. I have the following test case:
import asyncio
from threading import Thread
loop = asyncio.get_event_loop()
thread = Thread(target=loop.run_forever)
thread.start()
print('Started!')
loop.stop()
print('Requested stop!')
thread.join()
print('Finished!')
This test case prints:
Started!
Requested stop!
So, the test seems to block on thread.join()
, waiting for the event loop to terminate.
If I dump my threads I get the following:
Thread 0x00007000087ec000 (most recent call first):
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/selectors.py", line 577 in select
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/base_events.py", line 1388 in _run_once
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/asyncio/base_events.py", line 421 in run_forever
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/threading.py", line 862 in run
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/threading.py", line 914 in _bootstrap_inner
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/threading.py", line 882 in _bootstrap
Current thread 0x00007fffc6b273c0 (most recent call first):
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/threading.py", line 1070 in _wait_for_tstate_lock
File "/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/threading.py", line 1054 in join
I haven't looked to deeply at the Python code, but selectors.py
seems to be waiting for work. I guess it's possible that this problem is occurring because I've called stop
while there is no more work for the event loop to so, but this seems like it could be quite a problematic limitation.
Or maybe I've misunderstood something about how this should work?
Documentation says about event loop class:
This class is not thread safe.
And further:
An event loop runs in a thread and executes all callbacks and tasks in the same thread. [...] To schedule a callback from a different thread, the AbstractEventLoop.call_soon_threadsafe() method should be used. Example:
loop.call_soon_threadsafe(callback, *args)
Seems to be what we need:
import asyncio
from threading import Thread
loop = asyncio.get_event_loop()
thread = Thread(target=loop.run_forever)
thread.start()
print('Started!')
loop.call_soon_threadsafe(loop.stop) # here
print('Requested stop!')
thread.join()
print('Finished!')
Prints:
Started!
Requested stop!
Finished!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With