ProgrammazioneSviluppatore Backend

Come funziona il meccanismo defer nei cicli e nelle funzioni lambda in Go? Quali possono essere i rischi nell'utilizzare defer all'interno di un ciclo?

Supera i colloqui con l'assistente IA Hintsage

Risposta

defer rinvia l'esecuzione di una funzione fino all'uscita dalla funzione circostante, anche se l'uscita avviene a causa di un panico o di un return. Quando defer viene utilizzato all'interno di un ciclo, tutte le chiamate defer accumulate nella pila delle chiamate defer vengono eseguite in ordine inverso al termine della funzione circostante.

Questo può portare a un utilizzo imprevisto delle risorse e a ritardi, poiché tutte le funzioni defer verranno chiamate simultaneamente solo dopo l'uscita dal corpo della funzione, non dopo ogni iterazione del ciclo.

Esempio di codice:

func readFiles(files []string) { for _, name := range files { f, _ := os.Open(name) defer f.Close() // le risorse vengono rilasciate solo al termine dell'intera funzione // elaborazione del file ... } }

In questo esempio, i file rimarranno aperti fino al termine dell'esecuzione della funzione, il che può portare a perdite di descrittori quando ci sono molti file.

Domanda insidiosa

Cosa succede se si utilizza defer per chiudere le risorse all'interno di un ciclo? Perché non è sempre ottimale?

Risposta: Tutte le chiamate defer si accumulano e si attivano solo dopo la conclusione della funzione, non dopo ogni iterazione. Ciò comporterà che le risorse (ad esempio, i file aperti) verranno rilasciate troppo tardi.

Corretto:

for _, name := range files { f, _ := os.Open(name) // defer f.Close() // non è possibile! // Corretto: process(f) f.Close() }

Esempi di errori reali a causa della mancanza di conoscenza delle sfumature dell'argomento


Storia

Nel progetto di caricamento dei log, si è verificato un problema: il servizio ha smesso improvvisamente di aprire nuovi file, anche se ce n'erano pochi. La causa è stata defer nel ciclo. Tutti i file venivano aperti e la loro chiusura veniva rinviata fino alla fine della funzione. Dopo la riscrittura con Close() esplicito dopo l'elaborazione, il problema è scomparso.


Storia

Nel servizio che raccoglie le metriche per grandi liste, è stato utilizzato defer per il rilascio della connessione al database all'interno del ciclo di iterazione dei dati. Con l'aumento delle iterazioni, si sono verificate delle latenze e il superamento della soglia delle connessioni aperte, il servizio ha iniziato a bloccarsi con errori "too many open connections".


Storia

Un ingegnere, contando su una pulizia "elegante", ha applicato defer nel ciclo di lettura di un gran numero di file, il che ha portato a un sovraccarico del limite dei descrittori aperti sul server in produzione e all'arresto del servizio.