


Refactor : GoroutineTracker avec utilisation inutile de Reflect
Aujourd'hui, j'ai rencontré ce code dans la base de code de mon entreprise (le code et les commentaires sont réécrits à des fins de démonstration et n'incluent aucun code propriétaire) :
type GoroutineTracker struct { wg sync.WaitGroup // ... some other fields } // Go starts a new goroutine and tracks it with some metrics. func (g *GoroutineTracker) Go(ctx context.Context, name string, f any, args ...any) { fn := reflect.TypeOf(f) if fn.Kind() != reflect.Func { panic("must be function") } if fn.NumIn() != len(args) { panic("args does not match fn signature") } if fn.NumOut() > 0 { panic("output from fn is ignored") } g.wg.Add(1) id := g.startCaptureTime() go func() { defer func() { r := recover() // ... some panic handling code g.wg.Done() g.endCaptureTime(id) }() input := typez.MapFunc(args, func(arg any) reflect.Value { return reflect.ValueOf(arg) }) _ = reflect.ValueOf(f).Call(input) }() } // Wait for all goroutines to finished. func (g *GoroutineTracker) Wait() { g.wg.Wait() }
Le GoroutineTracker est utilisé pour suivre l'utilisation des goroutines dans la base de code, par exemple, le nombre de goroutines, le temps pris par chaque goroutine, etc. La méthode Go est utilisée pour démarrer une nouvelle goroutine et la suivre. La méthode Wait est utilisée pour attendre la fin de toutes les goroutines.
Exemple d'utilisation :
g := NewGoroutineTracker() g.Go(ctx, "task1", doTask1, arg1, arg2) g.Go(ctx, "task2", doTask2, arg3) g.Wait()
Problème : l'utilisation de la réflexion est inutile et peut être évitée
Eh bien, ce code fonctionne, mais il utilise le package Reflect pour vérifier la signature de la fonction puis appeler la fonction. C'est totalement inutile, et nous pouvons l'éviter en changeant l'usage en :
g := NewGoroutineTracker() g.Go(ctx, "task1", func() error { return doTask1(arg1, arg2) }) g.Go(ctx, "task2", func() error { return doTask2(arg3) })
Le nouveau code sera plus simple et présente de nombreux avantages :
- Sécurité du type : Pas besoin de vérifier la signature de la fonction à l'aide de Reflect. Le compilateur le fera pour nous. Le code d'origine comporte des erreurs d'exécution potentielles si la signature de la fonction ne correspond pas aux arguments.
- Gestion des erreurs : Nous pouvons renvoyer une erreur de la fonction et la gérer dans l'appelant. Le code d'origine ignore la sortie de la fonction.
- Lisibilité : Le nouveau code est plus lisible et plus facile à comprendre. Nous pouvons voir la signature de la fonction et les arguments directement dans le code.
Une meilleure implémentation de GoroutineTracker
Voici le code refactorisé :
func (g *GoroutineTracker) Go(ctx context.Context, fn func() error) { g.wg.Add(1) id := g.startCaptureTime() go func() (err error) { defer func() { r := recover() // capture returned error and panic g.endCaptureTime(id, r, err) g.wg.Done() }() // just call the function, no reflect needed return fn() }() }
Attendez que toutes les goroutines soient terminées avant de vous arrêter
Un autre cas d'utilisation de GoroutineTracker consiste à attendre la fin de toutes les goroutines avant d'arrêter l'application. On peut donc avoir 2 types d'attente :
- Dans une fonction : En attente de la fin de toutes les goroutines locales.
- Lors de l'arrêt de l'application : attente de la fin de toutes les goroutines démarrées par n'importe quel GoroutineTracker.
Nous pouvons l'implémenter en ajoutant un tracker global et en faisant en sorte que n'importe quel tracker enregistre sa fonction auprès du tracker global :
type GlobalTracker struct { wg sync.WaitGroup // ... some other fields } type GoroutineTracker struct { parent *GlobalTracker wg sync.WaitGroup // ... some other fields } func (g *GlobalTracker) New() *GoroutineTracker { return &GoroutineTracker{parent: g} } func (g *GoroutineTracker) Go(ctx context.Context, fn func() error) { g.wg.Add(1) // use both parent and local wg g.parent.wg.Add(1) // to track the new goroutine id := g.startCaptureTime() go func() (err error) { defer func() { // ... g.endCaptureTime(id, r, err) g.wg.Done() g.parent.wg.Done() }() return fn() }() } func (g *GlobalTracker) WaitForAll() { g.wg.Wait() } func (g *GoroutineTracker) Wait() { g.wg.Wait() }
Et nous pouvons utiliser WaitForAll() pour attendre que toutes les goroutines soient terminées avant de fermer l'application :
type FooService { tracker *GlobalTracker // ... some other fields } func (s *FooService) DoSomething(ctx context.Context) { g := s.tracker.New() g.Go(ctx, func() error { return s.doTask1(arg1, arg2) }) g.Go(ctx, func() error { return s.doTask2(arg3) }) g.Wait() // wait for local goroutines, this is optional } func main() { // some initialization, then start the application globalTracker := &GlobalTracker{} fooService := FooService{tracker: globalTracker, /*...*/} application.Start() // wait for all goroutines to finish before shutting down <-application.Done() globalTracker.Wait() }
Conclusion
En conclusion, bien que l'implémentation originale de GoroutineTracker fonctionne et puisse suivre les goroutines, son utilisation du package Reflect pour vérifier et appeler dynamiquement des fonctions introduit une complexité inutile et des erreurs d'exécution potentielles. En refactorisant le code pour accepter directement les littéraux de fonction, nous obtenons une sécurité de type améliorée, une gestion rationalisée des erreurs et une lisibilité améliorée. Cette approche exploite le système de types vérifiés par le compilateur Go pour garantir la compatibilité entre les signatures de fonction et les arguments, ce qui aboutit à un code plus robuste et plus maintenable. En adoptant ces changements, nous optimisons GoroutineTracker pour plus de clarté et de fiabilité, en nous alignant sur les meilleures pratiques de programmation Go.
Auteur
Je m'appelle Oliver Nguyen. Un éditeur de logiciels travaillant principalement en Go et JavaScript. J'aime apprendre et voir une meilleure version de moi-même chaque jour. Lancez occasionnellement de nouveaux projets open source. Partagez vos connaissances et vos réflexions tout au long de mon voyage.
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!

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

Video Face Swap
Échangez les visages dans n'importe quelle vidéo sans effort grâce à notre outil d'échange de visage AI entièrement gratuit !

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Sujets chauds











Golang est meilleur que Python en termes de performances et d'évolutivité. 1) Les caractéristiques de type compilation de Golang et le modèle de concurrence efficace le font bien fonctionner dans des scénarios de concurrence élevés. 2) Python, en tant que langue interprétée, s'exécute lentement, mais peut optimiser les performances via des outils tels que Cython.

Golang est meilleur que C en concurrence, tandis que C est meilleur que Golang en vitesse brute. 1) Golang obtient une concurrence efficace par le goroutine et le canal, ce qui convient à la gestion d'un grand nombre de tâches simultanées. 2) C Grâce à l'optimisation du compilateur et à la bibliothèque standard, il offre des performances élevées près du matériel, adaptées aux applications qui nécessitent une optimisation extrême.

GOISIDEALFORBEGINNERNERS et combinant pour pourcloudandNetWorkServicesDuetOtssimplicity, Efficiency, andCurrencyFeatures.1) InstallgofromTheofficialwebsiteandverifywith'goversion'..2)

Golang convient au développement rapide et aux scénarios simultanés, et C convient aux scénarios où des performances extrêmes et un contrôle de bas niveau sont nécessaires. 1) Golang améliore les performances grâce à des mécanismes de collecte et de concurrence des ordures, et convient au développement de services Web à haute concurrence. 2) C réalise les performances ultimes grâce à la gestion manuelle de la mémoire et à l'optimisation du compilateur, et convient au développement du système intégré.

Golang et Python ont chacun leurs propres avantages: Golang convient aux performances élevées et à la programmation simultanée, tandis que Python convient à la science des données et au développement Web. Golang est connu pour son modèle de concurrence et ses performances efficaces, tandis que Python est connu pour sa syntaxe concise et son écosystème de bibliothèque riche.

Les différences de performance entre Golang et C se reflètent principalement dans la gestion de la mémoire, l'optimisation de la compilation et l'efficacité du temps d'exécution. 1) Le mécanisme de collecte des ordures de Golang est pratique mais peut affecter les performances, 2) la gestion manuelle de C et l'optimisation du compilateur sont plus efficaces dans l'informatique récursive.

Golang et C ont chacun leurs propres avantages dans les compétitions de performance: 1) Golang convient à une concurrence élevée et à un développement rapide, et 2) C fournit des performances plus élevées et un contrôle fin. La sélection doit être basée sur les exigences du projet et la pile de technologie d'équipe.

GolangisidealforBuildingsCalableSystemsDuetoitSefficiency and Concurrency, tandis que les Implicites de l'Indrecosystem et le Golang'sDesignenCourageSlecElNCORES
