Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python asyncio: event loop does not seem to stop when stop method is called

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?

like image 621
Neil Avatar asked Sep 07 '17 09:09

Neil


1 Answers

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!
like image 74
Mikhail Gerasimov Avatar answered Nov 14 '22 05:11

Mikhail Gerasimov