From the official document, we see loop.stop()
while running loop.run_forever()
will run the current batch of callbacks and exit. Therefore, the following code works:
import asyncio
import signal
def signal_handler(sig, frame):
global loop
print('Cancelling tasks...')
loop.stop()
async def background_task():
while True:
print('Running background task...')
await asyncio.sleep(1)
async def main():
asyncio.create_task(background_task())
if __name__ == '__main__':
signal.signal(signal.SIGINT, signal_handler)
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(main())
loop.run_forever()
The result looks like:
% python test.py
Running background task...
Running background task...
^CCancelling tasks...
%
But if I use asyncio.to_thread
to create a multi-threading task, loop.stop()
fails to terminate the background task:
import asyncio
import signal
import time
def signal_handler(sig, frame):
global loop
print('Cancelling tasks...')
loop.stop()
def background_task():
while True:
print('Running background task...')
time.sleep(1)
async def main():
asyncio.create_task(asyncio.to_thread(background_task))
if __name__ == '__main__':
signal.signal(signal.SIGINT, signal_handler)
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(main())
loop.run_forever()
The result looks like
% python tests/test.py
Running background task...
Running background task...
^CCancelling tasks...
Running background task...
Running background task...
...
I’ve tried to add task.cancel()
before loop.stop()
, like this:
for task in asyncio.all_tasks():
task.cancel()
But it makes no difference.
I guess it’s because multi-threading tasks work in a different event loop, so calling stop
method of the loop where the background task runs may help.
But I don’t know how to get that event loop. It seems to be created automatically inside asyncio.to_thread
.
Also, if there are better alternative solutions to run a blocking background task, I’m very happy to hear.