Go
Verschieben
Suche…
Einführung
Eine defer
Anweisung drückt einen Funktionsaufruf in eine Liste. Die Liste der gespeicherten Anrufe wird ausgeführt, nachdem die umgebende Funktion zurückgegeben wurde. Defer wird häufig verwendet, um Funktionen zu vereinfachen, die verschiedene Bereinigungsaktionen ausführen.
Syntax
- someFunc (args) verschieben
- defer func () {// code geht hier hin} ()
Bemerkungen
Defer funktioniert, indem ein neuer Stack-Frame (die nach dem Schlüsselwort defer
aufgerufene Funktion) in den Aufruf-Stack unterhalb der aktuell ausgeführten Funktion eingefügt wird. Dies bedeutet, dass die Verzögerung verzögert wird, solange der Stapel abgewickelt wird (wenn Ihr Programm abstürzt oder einen SIGKILL
erhält, wird die Verzögerung nicht ausgeführt).
Defer Grundlagen
Eine verzögerte Anweisung in Go ist einfach ein Funktionsaufruf, der zu einem späteren Zeitpunkt ausgeführt wird. Defer-Anweisung ist ein gewöhnlicher Funktionsaufruf, dem das Schlüsselwort defer
vorangestellt ist.
defer someFunction()
Eine zurückgestellte Funktion wird ausgeführt, sobald die Funktion, die defer
Anweisung enthält, zurückgegeben wird. Der tatsächliche Aufruf der verzögerten Funktion erfolgt, wenn die einschließende Funktion:
- führt eine return-Anweisung aus
- fällt vom Ende ab
- Panik
Beispiel:
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)
}
Ausgabe:
First main statement
Last main statement
Function main returned
Wenn eine Funktion mehrere zurückgestellte Anweisungen hat, bilden sie einen Stapel. Der letzte defer
ist der erste, der ausgeführt wird, nachdem die einschließende Funktion zurückgegeben wurde, gefolgt von nachfolgenden Aufrufen der vorhergehenden defer
in der Reihenfolge (das folgende Beispiel führt zu einer Panik):
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)
}
Ausgabe:
First main statement
Num 3
Num 2
Num 1
panic: panic occurred
goroutine 1 [running]:
....
Beachten Sie, dass die Argumente für verzögerte Funktionen zum Zeitpunkt der Ausführung der defer
ausgewertet werden:
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)
}
Ausgabe:
First main statement
Num 4
Num 2
Num 1
Wenn eine Funktion Rückgabewerte benannt hat, kann eine zurückgestellte anonyme Funktion innerhalb dieser Funktion auf den zurückgegebenen Wert zugreifen und ihn aktualisieren, auch nachdem die Funktion zurückgegeben wurde:
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
}
Eine defer
ist in der Regel eine Operation, die häufig zusammen vorkommt. Zum Beispiel:
- öffnen und schließen Sie eine Datei
- verbinden und trennen
- sperren und entsperren Sie einen Mutex
- Eine Wartegruppe als erledigt markieren (
defer wg.Done()
)
Diese Verwendung gewährleistet die ordnungsgemäße Freigabe von Systemressourcen unabhängig vom Ausführungsfluss.
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close() // Body will always get closed
Aufgeschobene Funktionsaufrufe
Latente Funktionsaufrufe dienen einem ähnlichen Zweck , um Dinge wie finally
Blöcke in Sprachen wie Java: Sie sorgen dafür , dass eine bestimmte Funktion ausgeführt wird, wenn die äußeren Funktion zurückkehrt, unabhängig davon , ob ein Fehler aufgetreten ist oder welche Anweisung Rückkehr wurde in Fällen mit mehreren Rückkehr getroffen. Dies ist nützlich, um Ressourcen zu bereinigen, die wie Netzwerkverbindungen oder Dateizeiger geschlossen werden müssen. Das defer
Schlüsselwort gibt einen latenten Funktionsaufruf, ähnlich wie bei dem go
Begriff neu goroutine initiieren. Funktionsargumente werden wie ein go
Aufruf sofort ausgewertet, aber im Gegensatz zu einem go
Aufruf werden aufgeschobene Funktionen nicht gleichzeitig ausgeführt.
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
Beachten Sie die Verwendung von conn.Close()
statt conn.Close
- Sie sind nicht nur auf der Durch in einer Funktion, werden Sie eine volle Funktionsaufruf aufzuschieben, einschließlich seiner Argumente. Mehrere Funktionsaufrufe können in derselben äußeren Funktion zurückgestellt werden und werden jeweils einmal in umgekehrter Reihenfolge ausgeführt. Sie können Schließungen auch verschieben - vergessen Sie nicht die Parens!
defer func(){
// Do some cleanup
}()