Compteurs atomiques en Go concurrent

Beginner

This tutorial is from open-source community. Access the source code

Introduction

Ce laboratoire se concentre sur la gestion de l'état en Go en utilisant le package sync/atomic pour les compteurs atomiques accessibles par plusieurs goroutines.

Compteurs atomiques

Le problème consiste à incrémenter un compteur exactement 1000 fois en utilisant 50 goroutines et le package sync/atomic.

  • Utilisez le package sync/atomic pour incrémenter le compteur.
  • Utilisez un WaitGroup pour attendre que toutes les goroutines aient terminé leur travail.
## Nous nous attendons à obtenir exactement 50 000 opérations. Si nous
## avions utilisé l'incrémentation non atomique `ops++` pour incrémenter le compteur,
## nous obtiendrions probablement un nombre différent, variant d'une exécution
## à l'autre, car les goroutines se interféreraient mutuellement. De plus,
## nous obtiendrions des erreurs de course de données lorsque nous exécutons
## avec le drapeau `-race`.
$ go run atomic-counters.go
ops: 50000

## Ensuite, nous examinerons les mutexes, un autre outil pour gérer
## l'état.

Voici le code complet ci-dessous :

// Le principal mécanisme pour gérer l'état en Go est
// la communication via des canaux. Nous l'avons vu par exemple
// avec [les pools de travailleurs](worker-pools). Il existe cependant
// quelques autres options pour gérer l'état. Ici, nous allons
// examiner l'utilisation du package `sync/atomic` pour les _compteurs
// atomiques_ accessibles par plusieurs goroutines.

package main

import (
    "fmt"
    "sync"
    "sync/atomic"
)

func main() {

    // Nous utiliserons un entier non signé pour représenter notre
    // compteur (toujours positif).
    var ops uint64

    // Un WaitGroup nous aidera à attendre que toutes les goroutines
    // aient terminé leur travail.
    var wg sync.WaitGroup

    // Nous démarrerons 50 goroutines qui chacune incrémenteront le
    // compteur exactement 1000 fois.
    for i := 0; i < 50; i++ {
        wg.Add(1)

        go func() {
            for c := 0; c < 1000; c++ {
                // Pour incrémenter le compteur de manière atomique, nous
                // utilisons `AddUint64`, en lui donnant l'adresse mémoire
                // de notre compteur `ops` avec la syntaxe `&`.
                atomic.AddUint64(&ops, 1)
            }
            wg.Done()
        }()
    }

    // Attendez jusqu'à ce que toutes les goroutines soient terminées.
    wg.Wait()

    // Il est sûr d'accéder à `ops` maintenant car nous savons
    // qu'aucune autre goroutine n'écrit dessus. Il est également
    // possible de lire les atomiques de manière sûre pendant qu'elles
    // sont mises à jour, en utilisant des fonctions telles que
    // `atomic.LoadUint64`.
    fmt.Println("ops:", ops)
}

Résumé

Dans ce laboratoire, nous avons appris à utiliser le package sync/atomic pour gérer l'état en Go en incrémentant un compteur à l'aide de plusieurs goroutines. La fonction AddUint64 a été utilisée pour incrémenter le compteur de manière atomique, et un WaitGroup a été utilisé pour attendre que toutes les goroutines aient terminé leur travail.