21

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?

3
  • 1
    Why do you use asyncio in conjunction with a Thread? Commented Sep 7, 2017 at 11:27
  • @stovfl -- this example needs a separate thread. Since run_forever blocks, the only way to stop the loop is to do so in a separate thread. Commented Sep 7, 2017 at 16:03
  • @stovfl there's no need to be too religious about this. sometimes you have some stuff and you realize it makes sense to extract some of it into another thread, and it just happens to be best done async. that's how i ended up here, for example Commented May 10, 2023 at 18:53

1 Answer 1

50

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! 
Sign up to request clarification or add additional context in comments.

2 Comments

That's great, thanks. The asyncio API is a little unintuitive in places! You can't possibly call stop from the same thread as the running event loop, yet it doesn't work when you call from a different thread, without using call_soon_threadsafe.
Yes, you can most certainly call stop from the same thread as the running event loop—do so from a task which is being run on that event loop.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.