Maison >développement back-end >Golang >Pourquoi `sync.Once` utilise-t-il `atomic.StoreUint32` au lieu d'une affectation régulière pour l'indicateur `done` ?

Pourquoi `sync.Once` utilise-t-il `atomic.StoreUint32` au lieu d'une affectation régulière pour l'indicateur `done` ?

Barbara Streisand
Barbara Streisandoriginal
2024-10-31 05:40:01768parcourir

Why does `sync.Once` use `atomic.StoreUint32` instead of a regular assignment for the `done` flag?

AtomicStoreUint32 vs. Assignment in Sync.Once

Lors de l'exploration du code source du type sync.Once de Go, une question se pose concernant l'utilisation d'atomic.StoreUint32 par rapport à une affectation régulière pour définir l'indicateur terminé.

Implémentation incorrecte :

Le code source d'origine contenait une implémentation incorrecte :

<code class="go">func (o *Once) Do(f func()) {
    if atomic.CompareAndSwapUint32(&o.done, 0, 1) {
        f()
    }
}</code>

Cette implémentation ne garantit pas que f soit terminé au retour de la fonction. Des appels simultanés pourraient amener le gagnant à exécuter f tandis que le deuxième appelant revient immédiatement, en supposant que le premier appel soit terminé, ce qui peut ne pas être le cas.

Mise en œuvre correcte :

Pour remédier à ce problème, l'implémentation actuelle utilise atomic.StoreUint32 en conjonction avec un mutex :

<code class="go">func (o *Once) Do(f func()) {
    if atomic.LoadUint32(&o.done) == 0 {
        o.doSlow(f)
    }
}</code>
<code class="go">func (o *Once) doSlow(f func()) {
    o.m.Lock()
    defer o.m.Unlock()
    if o.done == 0 {
        defer atomic.StoreUint32(&o.done, 1)
        f()
    }
}</code>

Pourquoi AtomicStoreUint32 ?

L'utilisation d'atomic. .StoreUint32 est nécessaire pour garantir que d'autres goroutines peuvent observer le changement vers o.done une fois f terminé. Bien que les affectations primitives puissent être atomiques sur certaines architectures, le modèle de mémoire de Go nécessite l'utilisation du package atomique pour garantir les opérations atomiques sur toutes les architectures prises en charge.

Accès à Done Flag :

Le but est de s'assurer que l'accès au drapeau terminé est sécurisé en dehors du mutex. Par conséquent, les opérations atomiques sont utilisées au lieu du verrouillage avec un mutex. Cette optimisation améliore l'efficacité du chemin rapide, permettant le déploiement de sync.Once dans des scénarios à fort trafic.

Mutex pour doSlow :

Le mutex dans doSlow garantit qu'un seul appelant exécute f avant que o.done ne soit défini. atomic.StoreUint32 est utilisé pour écrire l'indicateur car il peut apparaître simultanément avec atomic.LoadUint32 en dehors de la section critique du mutex.

Écritures et lectures simultanées :

Lecture directe o.done dans doSlow est sûr grâce à la protection mutex. De plus, lire o.done simultanément avec atomic.LoadUint32 est sûr car les deux opérations impliquent uniquement la lecture.

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