UPD:
Sostituisci asyncio.ensure_future
con asyncio.create_task
ovunque se stai usando Python> = 3.7 È un modo più nuovo e più carino per generare attività .
asyncio.Task "sparare e dimenticare"
Secondo la documentazione di Python asyncio.Task
è possibile avviare alcune coroutine da eseguire "in background" . L'attività creata dalla asyncio.ensure_future
funzione non bloccherà l'esecuzione (quindi la funzione tornerà immediatamente!). Sembra un modo per "sparare e dimenticare" come richiesto.
import asyncio
async def async_foo():
print("async_foo started")
await asyncio.sleep(1)
print("async_foo done")
async def main():
asyncio.ensure_future(async_foo()) # fire and forget async_foo()
# btw, you can also create tasks inside non-async funcs
print('Do some actions 1')
await asyncio.sleep(1)
print('Do some actions 2')
await asyncio.sleep(1)
print('Do some actions 3')
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Produzione:
Do some actions 1
async_foo started
Do some actions 2
async_foo done
Do some actions 3
Cosa succede se le attività vengono eseguite dopo il completamento del ciclo di eventi?
Nota che asyncio si aspetta che l'attività venga completata nel momento in cui il ciclo di eventi è completato. Quindi, se passerai main()
a:
async def main():
asyncio.ensure_future(async_foo()) # fire and forget
print('Do some actions 1')
await asyncio.sleep(0.1)
print('Do some actions 2')
Riceverai questo avviso al termine del programma:
Task was destroyed but it is pending!
task: <Task pending coro=<async_foo() running at [...]
Per evitare ciò, puoi semplicemente attendere tutte le attività in sospeso dopo il completamento del ciclo di eventi:
async def main():
asyncio.ensure_future(async_foo()) # fire and forget
print('Do some actions 1')
await asyncio.sleep(0.1)
print('Do some actions 2')
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
# Let's also finish all running tasks:
pending = asyncio.Task.all_tasks()
loop.run_until_complete(asyncio.gather(*pending))
Uccidi i compiti invece di aspettarli
A volte non si desidera attendere il completamento delle attività (ad esempio, alcune attività potrebbero essere create per essere eseguite all'infinito). In tal caso, puoi semplicemente cancellarli () invece di aspettarli:
import asyncio
from contextlib import suppress
async def echo_forever():
while True:
print("echo")
await asyncio.sleep(1)
async def main():
asyncio.ensure_future(echo_forever()) # fire and forget
print('Do some actions 1')
await asyncio.sleep(1)
print('Do some actions 2')
await asyncio.sleep(1)
print('Do some actions 3')
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
# Let's also cancel all running tasks:
pending = asyncio.Task.all_tasks()
for task in pending:
task.cancel()
# Now we should await task to execute it's cancellation.
# Cancelled task raises asyncio.CancelledError that we can suppress:
with suppress(asyncio.CancelledError):
loop.run_until_complete(task)
Produzione:
Do some actions 1
echo
Do some actions 2
echo
Do some actions 3
echo