Skip to content

once

sync.Once est un outil de synchronisation de la bibliothèque standard Go, utilisé pour garantir qu'une fonction ne sera exécutée qu'une seule fois dans un environnement concurrent. Il est généralement utilisé pour l'initialisation différée, l'initialisation de ressources globales, etc., garantissant qu'une opération spécifique ne sera exécutée qu'une seule fois, même si plusieurs coroutines l'exécutent de manière concurrente.

go
package main

import (
	"fmt"
	"sync"
)

func main() {
	var wg sync.WaitGroup
	var once sync.Once
	for i := range 10 {
		wg.Add(1)
		go func() {
			defer wg.Done()
			once.Do(func() {
				fmt.Println(i)
			})
		}()
	}
	wg.Wait()
}

Dans le code ci-dessus, il y a 10 coroutines au total, mais peu importe les circonstances, une seule coroutine exécutera le fmt.Println(i) dans once.Do(). Laquelle exactement imprimera dépend de quelle coroutine atteint once.Do() en premier.

Structure

go
type Once struct {
    done atomic.Uint32
    m    Mutex
}

Sa structure interne est très simple :

  1. done, une valeur atomique, utilisée pour indiquer si l'exécution a déjà eu lieu
  2. m, verrou mutuel, utilisé pour bloquer les autres coroutines qui veulent exécuter

Son principe est simplement de verrouiller avant l'exécution, puis de mettre à jour done, et enfin de déverrouiller après l'exécution.

Do

go
func (o *Once) Do(f func()) {
	if o.done.Load() == 0 {
		o.doSlow(f)
	}
}

func (o *Once) doSlow(f func()) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.done.Load() == 0 {
		defer o.done.Store(1)
		f()
	}
}

Le code est globalement très simple, le processus est le suivant :

  1. Charge directement la valeur atomique, si déjà exécutée, retourne directement
  2. Si non exécutée, essaie d'obtenir le verrou, ici plusieurs coroutines peuvent être en compétition pour le verrou mutuel
  3. Quand le gagnant obtient le verrou, il doit encore vérifier la valeur de done, car après avoir obtenu le verrou, un autre a peut-être déjà terminé la séquence verrouillage-exécution-déverrouillage, et vous venez d'être réveillé
  4. Exécute la fonction cible
  5. Met à jour la valeur done
  6. Libère le verrou

Résumé

sync.Once est un outil de synchronisation très simple et efficace. Il garantit qu'une opération ne sera exécutée qu'une seule fois dans un environnement concurrent, évitant ainsi le travail répétitif ou le gaspillage de ressources.

Golang by www.golangdev.cn edit