Maison >développement back-end >Golang >Où dois-je placer les instructions « defer » dans une boucle pour libérer correctement les ressources dans Go ?

Où dois-je placer les instructions « defer » dans une boucle pour libérer correctement les ressources dans Go ?

Susan Sarandon
Susan Sarandonoriginal
2025-01-02 17:21:40611parcourir

Where Should I Place `defer` Statements in a Loop to Properly Release Resources in Go?

Comment libérer des ressources avec un report dans une boucle : la meilleure approche

Dans des contextes impliquant des requêtes de base de données dans une boucle, la question se pose comme au placement optimal des instructions defer pour garantir une libération appropriée des ressources. Considérez la boucle suivante :

for rows.Next() {

   fields, err := db.Query(.....)
   if err != nil {
      // ...
   }
   defer fields.Close()

   // do something with `fields`

}

Deux options pour différer le placement émergent :

  1. Différer dans le corps de la boucle :

    for rows.Next() {
    
       fields, err := db.Query(.....)
       if err != nil {
          // ...
       }
    
       // do something with `fields`
    }
    
    defer fields.Close()
  2. Différer après la boucle body :

    for rows.Next() {
    
       fields, err := db.Query(.....)
       if err != nil {
          // ...
       }
       
       // do something with `fields`
       
       defer fields.Close()
    }

Comprendre l'exécution différée

Pour choisir la meilleure approche, nous devons d'abord comprendre le comportement du report. Les fonctions différées sont non seulement retardées jusqu'au retour de la fonction environnante, mais sont également exécutées même si la fonction se termine brusquement en raison d'une exception (par exemple, panique). Cela constitue un mécanisme essentiel pour garantir la libération des ressources même dans des situations exceptionnelles.

Problèmes potentiels liés au placement différé

Placer le report à l'intérieur de la boucle peut entraver la libération des ressources à la fin de la boucle. . Si la boucle se termine prématurément en raison d'une erreur gérée dans la boucle, l'appel deferred field.Close() ne sera pas exécuté.

À l'inverse, placer defer après le corps de la boucle garantit que les ressources seront libérées quelle que soit la manière dont la boucle se termine. Cependant, cette approche retarde le nettoyage des ressources jusqu'à ce que la boucle entière soit terminée, ce qui peut ne pas être souhaitable dans tous les scénarios.

Solution optimale : Wrapper de fonction anonyme ou nommée

Pour Pour résoudre les deux problèmes, la solution recommandée consiste à encapsuler l'allocation et la libération des ressources dans une fonction anonyme ou nommée. Ce faisant, le report peut être utilisé au sein de la fonction pour garantir la libération des ressources au retour de la fonction.

Par exemple :

// Anonymous function wrapper
for rows.Next() {
    func() {
        fields, err := db.Query(...)
        if err != nil {
            // Handle error and return
            return
        }
        defer fields.Close()

        // do something with `fields`
    }()
}

// Named function wrapper
func foo(rs *db.Rows) {
    fields, err := db.Query(...)
    if err != nil {
        // Handle error and return
        return
    }
    defer fields.Close()

    // do something with `fields`
}

for rows.Next() {
    foo(rs)
}

Cette approche permet de libérer les ressources dès qu'elles ne le sont plus. nécessaire, même en cas d'exceptions. De plus, si l'objectif est de terminer la boucle à la première erreur, l'erreur peut être renvoyée par la fonction wrapper et gérée en conséquence :

func foo(rs *db.Rows) error {
    fields, err := db.Query(...)
    if err != nil {
        return fmt.Errorf("db.Query error: %w", err)
    }
    defer fields.Close()

    // do something with `fields`
    return nil
}

for rows.Next() {
    if err := foo(rs); err != nil {
        // Handle error and return
        return
    }
}

Gestion des erreurs avec Rows.Close()

Il est important de noter que Rows.Close() renvoie une erreur. Pour gérer cette erreur, une fonction anonyme avec un appel différé à Rows.Close() peut être utilisée :

func foo(rs *db.Rows) (err error) {
    fields, err := db.Query(...)
    if err != nil {
        return fmt.Errorf("db.Query error: %w", err)
    }
    defer func() {
        if err = fields.Close(); err != nil {
            err = fmt.Errorf("Rows.Close() error: %w", err)
        }
    }()

    // do something with `fields`
    return nil
}

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn