ProgrammazioneSviluppatore Perl Senior, Backend/Fullstack

Spiega le peculiarità del lavoro con le chiusure (closures) e le subroutine anonime in Perl. Come usarle correttamente, dove sorgono i punti delicati e come evitare perdite di memoria?

Supera i colloqui con l'assistente IA Hintsage

Risposta

In Perl sono supportate le subroutine anonime e le chiusure. Le subroutine anonime sono dichiarate tramite sub senza nome e restituiscono un riferimento al codice. Una chiusura è una subroutine che "cattura" l'ambito lessicale, comprese le variabili esistenti al momento della sua creazione.

Esempio di subroutine anonima e closure:

sub make_incrementer { my $inc = shift; return sub { $_[0] + $inc }; } my $inc10 = make_incrementer(10); print $inc10->(5); # Restituisce 15

Le chiusure sono ampiamente utilizzate per creare fabbriche di funzioni, generatori e callback (ad esempio, in map/grep, gestori di eventi).

Un punto importante: se una chiusura fa riferimento a variabili che, a loro volta, puntano alla stessa chiusura (direttamente o tramite una struttura), si verifica un riferimento ciclico e potrebbe verificarsi una perdita di memoria.

Domanda insidiosa

Quando vengono liberate le variabili chiuse in una closure? C'è differenza nel comportamento tra my e our?

Risposta: Le variabili my, chiuse all'interno di una closure, rimangono vive fino a quando esiste almeno un riferimento alla stessa closure. Non vengono liberate al termine della funzione, la loro durata è quella dell'intera vita della closure. Per le variabili our non esiste questo comportamento: sono accessibili a tutte le closure e vengono liberate al termine del programma. Dimenticando di eliminare la closure, si possono ottenere perdite di memoria.

Esempio di problema:

my $cref; { my $val = 5; $cref = sub { $val++ }; } # $val esiste ancora finché $cref è vivo

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


Storia

In un'applicazione server, per ogni utente veniva creata una chiusura di contesto per callback. Ma la closure faceva anche riferimento alla struttura user, il che portava a un riferimento ciclico. A causa di ciò, il garbage collector non puliva gli oggetti utente anche dopo il logout: le perdite di memoria crescevano esponenzialmente.


Storia

In un'applicazione demone per l'elaborazione in background degli eventi, sono state utilizzate chiusure con variabili contatore chiuse, ma si era dimenticati di azzerare gli array a cui facevano riferimento. Il risultato è stato che, a causa di un paio di chiusure dimenticate, si accumulavano accidentalmente vecchi dati di messaggi, fino a dover effettuare una pulizia manuale della heap.


Storia

Un sviluppatore ha tentato di utilizzare una variabile our in una closure, aspettandosi un comportamento "chiuso" - ma tutte le closure condividevano la stessa variabile, portando a condizioni di gara durante l'esecuzione parallela. Il bug si è manifestato durante l'utilizzo simultaneo da parte di utenti con parametri diversi (calcoli errati).