Beheer met uitstellen, panieken en herstelfuncties

Voltooid

Laten we nu eens kijken naar enkele besturingsstromen die uniek zijn voor Go: defer, panicen recover. Elk van deze functies heeft verschillende use cases. We verkennen hier de belangrijkste use cases.

Functie uitstellen

In Go stelt een defer instructie het uitvoeren van een functie (inclusief parameters) uit totdat de functie met de defer instructie is voltooid. Over het algemeen uitstelt u een functie wanneer u wilt voorkomen dat taken worden vergeten, zoals het sluiten van een bestand of het uitvoeren van een opschoonproces.

U kunt zoveel functies uitstellen als u wilt. De defer instructies worden in omgekeerde volgorde uitgevoerd, van laatste naar eerste.

Bekijk hoe dit patroon werkt door de volgende voorbeeldcode uit te voeren:

package main

import "fmt"

func main() {
    for i := 1; i <= 4; i++ {
        defer fmt.Println("deferred", -i)
        fmt.Println("regular", i)
    }
}

Dit is de code-uitvoer:

regular 1
regular 2
regular 3
regular 4
deferred -4
deferred -3
deferred -2
deferred -1

In dit voorbeeld ziet u dat elke keer fmt.Println("deferred", -i) dat deze is uitgesteld, de waarde voor i is opgeslagen en dat de functieaanroep is toegevoegd aan een wachtrij. Nadat de functie de main() waarden heeft afgedrukt regular , zijn alle uitgestelde aanroepen uitgevoerd. U ziet dat de uitvoer van de uitgestelde aanroepen omgekeerd is (laatste in, eerste uit), omdat deze uit de wachtrij worden gehaald.

Een typische use case voor de defer functie is om een bestand te sluiten nadat u het hebt gebruikt. Hier volgt een voorbeeld:

package main

import (
    "io"
    "os"
    "fmt"
)

func main() {
    newfile, error := os.Create("learnGo.txt")
    if error != nil {
        fmt.Println("Error: Could not create file.")
        return
    }
    defer newfile.Close()

    if _, error = io.WriteString(newfile, "Learning Go!"); error != nil {
	    fmt.Println("Error: Could not write to file.")
        return
    }

    newfile.Sync()
}

Nadat u een bestand hebt gemaakt of geopend, kunt u de .Close() functie uitstellen om te voorkomen dat u het bestand sluit nadat u klaar bent.

Paniekfunctie

Runtimefouten zorgen ervoor dat een Go-programma in paniek raakt, zoals een poging om toegang te krijgen tot een matrix met behulp van een niet-afhankelijke index of het uitstellen van een nil-aanwijzer. Je kunt ook afdwingen dat een programma in paniek raakt.

De ingebouwde functie stopt de normale controlestroom in een Go-programma panic() . Wanneer u een panic aanroep gebruikt, worden alle uitgestelde functieaanroepen normaal uitgevoerd. Het proces gaat door met de stack totdat alle functies worden geretourneerd. Het programma loopt vervolgens vast met een logboekbericht. Het bericht bevat foutinformatie en een stack-trace om u te helpen de hoofdoorzaak van het probleem vast te stellen.

Wanneer u de panic() functie aanroept, kunt u elke waarde als argument toevoegen. Meestal stuurt u een foutbericht over waarom u in paniek raakt.

Met de volgende code worden bijvoorbeeld de panic en defer functies gecombineerd. Voer deze code uit om te zien hoe de controlestroom wordt onderbroken. U ziet dat de opschoonprocessen nog steeds worden uitgevoerd.

package main

import "fmt"

func highlow(high int, low int) {
    if high < low {
        fmt.Println("Panic!")
        panic("highlow() low greater than high")
    }
    defer fmt.Println("Deferred: highlow(", high, ",", low, ")")
    fmt.Println("Call: highlow(", high, ",", low, ")")

    highlow(high, low + 1)
}

func main() {
    highlow(2, 0)
    fmt.Println("Program finished successfully!")
}

Dit is de uitvoer:

Call: highlow( 2 , 0 )
Call: highlow( 2 , 1 )
Call: highlow( 2 , 2 )
Panic!
Deferred: highlow( 2 , 2 )
Deferred: highlow( 2 , 1 )
Deferred: highlow( 2 , 0 )
panic: highlow() low greater than high

goroutine 1 [running]:
main.highlow(0x2, 0x3)
	/tmp/sandbox/prog.go:13 +0x34c
main.highlow(0x2, 0x2)
	/tmp/sandbox/prog.go:18 +0x298
main.highlow(0x2, 0x1)
	/tmp/sandbox/prog.go:18 +0x298
main.highlow(0x2, 0x0)
	/tmp/sandbox/prog.go:18 +0x298
main.main()
	/tmp/sandbox/prog.go:6 +0x37

Program exited: status 2.

Dit gebeurt wanneer de code wordt uitgevoerd:

  1. Alles draait normaal. Het programma drukt de hoge en lage waarden af die in de highlow() functie zijn doorgegeven.

  2. Wanneer de waarde low groter is dan de waarde van high, raakt het programma in paniek. U ziet het Panic! bericht. Op dit moment wordt de controlestroom onderbroken en beginnen alle uitgestelde functies om het Deferred... bericht af te drukken.

  3. Het programma loopt vast en u ziet de volledige stacktracering. U ziet het Program finished successfully! bericht niet.

Een aanroep van de panic() functie wordt meestal uitgevoerd wanneer er geen ernstige fouten worden verwacht. Als u een programmacrash wilt voorkomen, kunt u een andere functie met de naam recover()gebruiken.

Functie Herstellen

Soms wilt u het vastlopen van een programma voorkomen en in plaats daarvan de fout intern rapporteren. Of misschien wilt u de puinhoop opschonen voordat u het programma laat crashen. U kunt bijvoorbeeld een verbinding met een resource sluiten om meer problemen te voorkomen.

Go biedt de ingebouwde recover() functie waarmee u na paniek weer controle kunt krijgen. U roept recover alleen een functie aan waar u ook aanroept defer. Als u de recover() functie aanroept nil , wordt deze geretourneerd en heeft dit geen ander effect bij normale uitvoering.

Probeer de main functie in de vorige code te wijzigen om een aanroep aan de recover() functie toe te voegen, zoals hieronder:

func main() {
    defer func() {
	handler := recover()
        if handler != nil {
            fmt.Println("main(): recover", handler)
        }
    }()

    highlow(2, 0)
    fmt.Println("Program finished successfully!")
}

Wanneer u het programma uitvoert, moet de uitvoer er als volgt uitzien:

Call: highlow( 2 , 0 )
Call: highlow( 2 , 1 )
Call: highlow( 2 , 2 )
Panic!
Deferred: highlow( 2 , 2 )
Deferred: highlow( 2 , 1 )
Deferred: highlow( 2 , 0 )
main(): recover from panic highlow() low greater than high

Program exited.

Ziet u het verschil met de vorige versie? Het belangrijkste verschil is dat u de stack-traceringsfout niet meer ziet.

In de main() functie kunt u een anonieme functie uitstellen waar u de recover() functie aanroept. Er kan geen oproep worden recover() geretourneerd nil wanneer het programma in paniek raakt. U kunt hier iets doen om de rommel op te schonen, maar in dit voorbeeld drukt u gewoon iets af.

De combinatie van de panic en recover functies is de idiomatische manier waarop Go uitzonderingen verwerkt. In andere programmeertalen wordt het try/catch blok gebruikt. Go geeft de voorkeur aan de benadering die u hier hebt verkend.

Bekijk het voorstel voor het toevoegen van een ingebouwde try functie in Go voor meer informatie.