It strikes me that the run_in_executor()
method of the asyncio
library belongs to a loop
object.
In particular, what would be the difference if I chose to run a second thread alongside an async event loop the "usual" way, by import threading
, t = threading.Thread(target=...)
, t.start()
?
Perhaps the answer is by using the asyncio
module there are low level optimizations which can be made at runtime if the loop knows about additional threads?
run_in_executor is used to manage threads from within an event loop. To this end, it needs to wrap the thread into a Future, which needs to be assigned to an event loop (in one way or another). The reason the method is stored directly in a loop object is probably historical.
Run the event loop until stop() is called. If stop() is called before run_forever() is called, the loop will poll the I/O selector once with a timeout of zero, run all callbacks scheduled in response to I/O events (and those that were already scheduled), and then exit.
Introduction to Python Event Loop. Python Event Loop is the centre of each asyncio application. Occasion circles, run offbeat assignments and callbacks, perform arrange IO activities, and run subprocesses. Python Event Loop is useful to deal with all the occasions in a computational code.
Run an asyncio Event Loop run_until_complete(<some Future object>) – this function runs a given Future object, usually a coroutine defined by the async / await pattern, until it's complete. run_forever() – this function runs the loop forever. stop() – the stop function stops a running loop.
You can always start another thread manually, but then you are responsible for giving it work, e.g. using a queue. In Python 3 concurrent.futures
provide a convenient API for offloading tasks to a thread pool, which it calls executor. Its submit
method takes a function, gives it to a thread in the pool to run it, and immediately returns a handle that will provide the result (or propagate an exception) when it is ready.
run_in_executor
delivers the same convenience to asyncio. Remember that you're not supposed to run any blocking code inside asyncio - for example time.sleep()
is forbidden, because it blocks the whole event loop. run_in_executor
allows you to side-step that rule. For example:
async def sleep_test():
loop = asyncio.get_event_loop()
print('going to sleep')
await loop.run_in_executor(None, time.sleep, 5)
#time.sleep(5)
print('waking up')
async def parallel():
# run two sleep_tests in parallel and wait until both finish
await asyncio.gather(sleep_test(), sleep_test())
asyncio.run(parallel())
Running this code shows that both instances of the coroutine sleep in parallel. If we used time.sleep()
directly, they would sleep in series because the sleep would block the event loop.
This example is of course silly because there is asyncio.sleep()
that suspends a coroutine without spending a slot in a thread pool, but it shows the basic idea. Realistic use cases for run_in_executor
include:
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