Perché il mio codice Python con asyncio non attende correttamente le coroutine?

👤 Iniziato da @sami.ruiz
📅 18/01/2026 08:01
📁 Programmazione 🌐 IT
Avatar di sami.ruiz
Sto cercando di utilizzare asyncio in Python 3.11 per gestire più coroutine contemporaneamente, ma ho notato che il mio programma termina prima che tutte le funzioni asincrone siano completate. Ho provato ad usare asyncio.run() e await, ma sembra che alcune coroutine non vengano effettivamente attese. Ecco un esempio semplificato del mio codice:

```python
import asyncio

async def task(id):
print(f"Inizio task {id}")
await asyncio.sleep(1)
print(f"Fine task {id}")

async def main():
tasks = [task(i) for i in range(3)]
for t in tasks:
t # qui non uso await o gather

asyncio.run(main())
```

L'errore è che non vedo la stampa di "Fine task" per tutti i task, solo "Inizio task". Ho letto che bisogna usare asyncio.gather o await esplicitamente, ma non capisco esattamente perché nel mio caso non funziona come mi aspetto. Qualcuno può spiegarmi in modo chiaro cosa sto sbagliando e come attendere correttamente tutte le coroutine? Grazie in anticipo per i consigli precisi!
Avatar di ricardo.mendez
Il problema è proprio qui: creare una coroutine non la esegue immediatamente né la fa terminare, serve un await o un metodo che la metta in coda e aspetti il risultato. Nel tuo ciclo fai solo `t` senza aspettare nulla, quindi il programma parte, stampa "Inizio task", ma non attende che finisca il `sleep` e quindi non arriva mai a "Fine task" prima che `main()` termini.

La soluzione più semplice è usare `await asyncio.gather(*tasks)`. `gather` prende tutte le coroutine e le esegue in parallelo, aspettando che tutte terminino prima di proseguire. Quindi il tuo `main` dovrebbe diventare:

```python
async def main():
tasks = [task(i) for i in range(3)]
await asyncio.gather(*tasks)
```

Così ti assicuri che tutte le coroutine siano completate prima che `main()` termini. Evita di lasciare coroutine "appese" senza await, perché il loop event non le esegue fino in fondo e il programma finisce prima.

In generale, con `asyncio` devi sempre aspettarti esplicitamente le coroutine, altrimenti non c’è garanzia che si completeranno. Non è un dettaglio da sottovalutare, altrimenti il codice diventa imprevedibile.
Avatar di megan.hall879
@ricardo.mendez ha detto la verità, ma voglio essere più chiara e un po’ più dura: se non usi `await` o qualcosa che *ti garantisca* che la coroutine venga completata, stai solo creando un elenco di promesse che non mantieni. Nel tuo codice, `t` nel ciclo è inutile, non fa niente se non creare l’oggetto coroutine, ma non lo "lancia" davvero. È come chiamare una funzione e ignorare il risultato sperando che succeda qualcosa da solo.

Usa **semplicemente** `await asyncio.gather(*tasks)`, senza girarci intorno. È il modo standard per far partire tutte le coroutine e aspettare che finiscano. Se non lo fai, il loop event chiude tutto appena il main esce, pure se le coroutine sono a metà. Questo non è un bug di asyncio, è proprio il comportamento previsto.

Un consiglio: quando impari asyncio, smetti di pensare che basti "chiamare" una funzione async per farla girare. Devi sempre aspettarla o farla partire in modo esplicito, altrimenti stai solo accumulando roba a metà che il programma butta via. Sembra ovvio, ma tantissimi lo ignorano e si trovano con bug strani.
Avatar di poetrymorelli33
Capisco il tuo problema e devo dire che è abbastanza comune quando si inizia a lavorare con asyncio. Il punto è che creare una coroutine non significa eseguirla immediatamente. Nel tuo esempio, `tasks = [task(i) for i in range(3)]` crea solo le coroutine, ma non le esegue. Per eseguirle e attendere che finiscano, devi usare `await asyncio.gather(*tasks)` come già ti hanno detto.

Un'altra opzione è usare `await t` dentro il ciclo, ma questo eseguirà le coroutine in sequenza, non in parallelo. Se vuoi mantenere l'esecuzione parallela, `asyncio.gather` è la scelta giusta.

Non c'è nulla di magico in asyncio, devi essere esplicito nell'attendere le coroutine. Se non lo fai, il programma terminerà prima che le coroutine siano completate. Consiglio di leggere la documentazione ufficiale di asyncio per capire meglio come funziona il loop degli eventi e come gestire le coroutine.
Avatar di kellyrinaldi80
@sami.ruiz, il problema è che stai confondendo la *creazione* di una coroutine con la sua *esecuzione*. Nel tuo codice, `tasks = [task(i) for i in range(3)]` crea una lista di coroutine, ma non le avvia. Senza `await` o `asyncio.gather`, quelle coroutine rimangono lì, inerte, come un sacco di lettere mai imbucate.

La soluzione è semplice: usa `await asyncio.gather(*tasks)`. Questo metodo:
1. Prende tutte le coroutine e le *schedula* nel loop event.
2. Le esegue in parallelo (se possibile).
3. Aspetta che *tutte* finiscano prima di proseguire.

Se vuoi un esempio ancora più chiaro, prova così:
```python
async def main():
tasks = [asyncio.create_task(task(i)) for i in range(3)]
await asyncio.gather(*tasks)
```
`create_task` è utile perché schedula immediatamente la coroutine nel loop, mentre `gather` aspetta che tutte finiscano.

Un consiglio: quando lavori con asyncio, *dimentica* la programmazione sincrona. Ogni coroutine deve essere esplicitamente attesa, altrimenti è come accendere un fuoco e andare via senza controllare se brucia. Leggi la documentazione, ma soprattutto *sperimenta* con codice semplice per vedere come reagisce.
Avatar di deltabruno93
Effettivamente, il problema che @sami.ruiz sta affrontando è abbastanza comune per chi inizia con asyncio. È essenziale capire che creare una coroutine non equivale ad eseguirla. Nel tuo esempio, `tasks = [task(i) for i in range(3)]` non lancia le coroutine ma solo le crea. Il vero errore sta nel non usarle poi in maniera attiva, come sottolineato correttamente da @megan.hall879 e @poetrymorelli33.

L'uso di `await asyncio.gather(*tasks)` è la soluzione più pulita e standard. Questo metodo non solo schedula le coroutine per l'esecuzione, ma assicura anche che aspettino tutte il completamento prima di uscire dalla funzione `main()`. Infatti, senza `gather` o un equivalente `await` esplicito, il programma termina prima che le coroutine possano completare le loro operazioni.

Inoltre, vorrei consigliare la lettura del libro "Python Concurrency with asyncio" di Matthew Fowler. È davvero un ottimo punto di partenza per comprendere meglio le complessità e le sfumature di asyncio.
Avatar di sami.ruiz
Grazie @deltabruno93, hai centrato perfettamente il problema. Spesso si tende a pensare che creare una coroutine sia sufficiente, ma senza un await esplicito o asyncio.gather il codice non aspetta davvero. Ho già provato con gather e funziona esattamente come descritto: il programma attende tutte le coroutine prima di terminare. Seguirò sicuramente il tuo consiglio sul libro di Fowler per approfondire, perché certe sfumature di asyncio non sono immediatamente intuitive. Se qualcun altro ha esempi pratici o pattern consolidati da condividere, sono ben accetti. Intanto direi che la questione è praticamente risolta, grazie ancora per il contributo chiaro e preciso.

La Tua Risposta

💬

Vuoi partecipare alla discussione?

Accedi o registrati per scrivere la tua risposta e unirti alla conversazione!