Go
Differire
Ricerca…
introduzione
defer
spinge una chiamata di funzione in una lista. L'elenco delle chiamate salvate viene eseguito dopo il ritorno della funzione circostante. Defer è comunemente usato per semplificare le funzioni che eseguono varie azioni di pulizia.
Sintassi
- differire someFunc (args)
- defer func () {// code goes here} ()
Osservazioni
Il differimento funziona iniettando un nuovo stack frame (la funzione chiamata dopo la parola chiave defer
) nello stack di chiamate sotto la funzione attualmente in esecuzione. Ciò significa che il differimento è garantito per l'esecuzione fino a quando lo stack sarà svolto (se il tuo programma si blocca o ottiene un SIGKILL
, il rinvio non verrà eseguito).
Defer Nozioni di base
Una dichiarazione di differimento in Go è semplicemente una chiamata di funzione contrassegnata per essere eseguita in un secondo momento. L'istruzione di defer
è una chiamata di funzione ordinaria preceduta dal defer
della parola chiave.
defer someFunction()
Una funzione differita viene eseguito una volta la funzione che contiene le defer
restituisce istruzione. La chiamata effettiva alla funzione differita si verifica quando la funzione di chiusura:
- esegue una dichiarazione di ritorno
- cade alla fine
- panico
Esempio:
func main() {
fmt.Println("First main statement")
defer logExit("main") // position of defer statement here does not matter
fmt.Println("Last main statement")
}
func logExit(name string) {
fmt.Printf("Function %s returned\n", name)
}
Produzione:
First main statement
Last main statement
Function main returned
Se una funzione ha più istruzioni posticipate, formano una pila. L'ultimo defer
è il primo da eseguire dopo il ritorno della funzione di inclusione, seguito dalle successive chiamate al precedente defer
in ordine (sotto l'esempio restituisce provocando un panico):
func main() {
defer logNum(1)
fmt.Println("First main statement")
defer logNum(2)
defer logNum(3)
panic("panic occurred")
fmt.Println("Last main statement") // not printed
defer logNum(3) // not deferred since execution flow never reaches this line
}
func logNum(i int) {
fmt.Printf("Num %d\n", i)
}
Produzione:
First main statement
Num 3
Num 2
Num 1
panic: panic occurred
goroutine 1 [running]:
....
Si noti che le funzioni posticipate hanno i loro argomenti valutati al momento defer
:
func main() {
i := 1
defer logNum(i) // deferred function call: logNum(1)
fmt.Println("First main statement")
i++
defer logNum(i) // deferred function call: logNum(2)
defer logNum(i*i) // deferred function call: logNum(4)
return // explicit return
}
func logNum(i int) {
fmt.Printf("Num %d\n", i)
}
Produzione:
First main statement
Num 4
Num 2
Num 1
Se una funzione ha denominato valori di ritorno, una funzione anonima differita all'interno di quella funzione può accedere e aggiornare il valore restituito anche dopo che la funzione è ritornata:
func main() {
fmt.Println(plusOne(1)) // 2
return
}
func plusOne(i int) (result int) { // overkill! only for demonstration
defer func() {result += 1}() // anonymous function must be called by adding ()
// i is returned as result, which is updated by deferred function above
// after execution of below return
return i
}
Infine, una dichiarazione di defer
viene generalmente utilizzata operazioni che spesso si verificano insieme. Per esempio:
- aprire e chiudere un file
- connettersi e disconnettersi
- bloccare e sbloccare un mutex
- segna un waitgroup come fatto (
defer wg.Done()
)
Questo utilizzo garantisce il corretto rilascio delle risorse di sistema indipendentemente dal flusso di esecuzione.
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close() // Body will always get closed
Chiamate a funzioni differite
Le chiamate a funzioni differite hanno uno scopo simile a cose come blocchi finally
in linguaggi come Java: assicurano che alcune funzioni vengano eseguite quando la funzione esterna ritorna, indipendentemente dal fatto che si sia verificato un errore o che sia stata rilasciata una dichiarazione di ritorno in caso di più resi. Questo è utile per ripulire le risorse che devono essere chiuse come connessioni di rete o puntatori di file. La parola chiave defer
indica una chiamata a una funzione differita, analogamente alla parola chiave go
avvia una nuova goroutine. Come una chiamata go
, gli argomenti delle funzioni vengono valutati immediatamente, ma a differenza di una chiamata go
, le funzioni posticipate non vengono eseguite contemporaneamente.
func MyFunc() {
conn := GetConnection() // Some kind of connection that must be closed.
defer conn.Close() // Will be executed when MyFunc returns, regardless of how.
// Do some things...
if someCondition {
return // conn.Close() will be called
}
// Do more things
}// Implicit return - conn.Close() will still be called
Nota l'uso di conn.Close()
invece di conn.Close
: non stai passando solo una funzione, stai rimandando una chiamata a una funzione completa, inclusi i suoi argomenti. Le chiamate a funzioni multiple possono essere rinviate nella stessa funzione esterna e ciascuna verrà eseguita una volta nell'ordine inverso. Puoi anche differire le chiusure - non dimenticare i paren!
defer func(){
// Do some cleanup
}()