Go
Uppskjuta
Sök…
Introduktion
Ett defer
uttalande skjuter ett funktionssamtal till en lista. Listan över sparade samtal körs när den omgivande funktionen har återgått. Uppskjutning används vanligtvis för att förenkla funktioner som utför olika saneringsåtgärder.
Syntax
- skjuta upp vissaFunc (args)
- skjuta upp func () {// kod går hit} ()
Anmärkningar
Uppskjutning fungerar genom att injicera en ny stapelram (den kallade funktionen efter defer
nyckelord) i samtalstaket under den nuvarande exekverande funktionen. Detta innebär att uppskjutning garanteras att köra så länge stapeln kommer att avvikas (om ditt program kraschar eller får en SIGKILL
, kommer inte uppskjutningen att köras).
Uppskjutna grunder
Ett uppskjutande uttalande i Go är helt enkelt ett funktionssamtal som är markerat för att utföras vid ett senare tillfälle. Uppskjutande uttalande är ett vanligt funktionssamtal förinställt av nyckelordet defer
.
defer someFunction()
En uppskjuten funktion exekveras när funktionen som innehåller defer
uttalande returneras. Det faktiska samtalet till den uppskjutna funktionen sker när den bifogade funktionen:
- verkställer ett avkastningsförklaring
- faller bort från slutet
- panik
Exempel:
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)
}
Produktion:
First main statement
Last main statement
Function main returned
Om en funktion har flera uppskjutna uttalanden, bildar de en stack. Den sista defer
är den första som körs efter att den medföljande funktionen återgår, följt av efterföljande samtal till föregående defer
i ordning (nedan exempel återgår genom att orsaka 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)
}
Produktion:
First main statement
Num 3
Num 2
Num 1
panic: panic occurred
goroutine 1 [running]:
....
Observera att uppskjutna funktioner har sina argument utvärderats vid tidpunkten för 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)
}
Produktion:
First main statement
Num 4
Num 2
Num 1
Om en funktion har namngivit returvärden, kan en uppskjuten anonym funktion inom den funktionen komma åt och uppdatera det returnerade värdet även efter att funktionen har returnerats:
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
}
Slutligen används ett defer
uttalande vanligtvis operationer som ofta förekommer tillsammans. Till exempel:
- öppna och stäng en fil
- ansluta och koppla bort
- låsa och låsa upp en mutex
- markera en väntgrupp som gjort (
defer wg.Done()
)
Denna användning säkerställer korrekt frisättning av systemresurser oberoende av flöde av körning.
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close() // Body will always get closed
Uppskjuten funktionssamtal
Uppskjutna funktionssamtal tjänar ett liknande syfte som saker som finally
blockerar i språk som Java: de säkerställer att någon funktion kommer att köras när den yttre funktionen återgår, oavsett om ett fel inträffade eller vilket returrätt som träffades i fall med flera returer. Detta är användbart för att rensa upp resurser som måste stängas som nätverksanslutningar eller filpekare. Den defer
nyckelordet indikerar en uppskjuten samtalsfunktion, i likhet med go
nyckelordet initierar en ny goroutine. Som en go
samtal är funktionsargument utvärderas omedelbart, men till skillnad från en go
samtal är uppskjutna funktioner inte exekveras samtidigt.
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
Observera användningen av conn.Close()
istället för conn.Close
- du passerar inte bara en funktion utan du skjuter upp ett fullfunktionssamtal , inklusive dess argument. Flera funktionssamtal kan skjutas upp i samma yttre funktion, och varje utförs en gång i omvänd ordning. Du kan också skjuta upp stängningar - glöm inte föräldrarna!
defer func(){
// Do some cleanup
}()