once
sync.Once è uno strumento di sincronizzazione nella libreria standard di Go, utilizzato per garantire che una funzione venga eseguita una sola volta in un ambiente concorrente. Viene comunemente utilizzato per l'inizializzazione lazy, l'inizializzazione di risorse globali e altri scenari, assicurando che una determinata operazione venga eseguita una sola volta, anche se più goroutine la eseguono concorrentemente.
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()
}Nel codice sopra, ci sono un totale di 10 goroutine, ma indipendentemente da tutto, solo una goroutine eseguirà fmt.Println(i) all'interno di once.Do(). Quale goroutine stamperà dipende da quale goroutine arriva per prima a once.Do().
Struttura
type Once struct {
done atomic.Uint32
m Mutex
}La sua struttura interna è molto semplice:
done, un valore atomico utilizzato per indicare se è stato eseguitom, mutex, utilizzato per bloccare altre goroutine che desiderano eseguire
Il suo principio è acquisire il lock prima dell'esecuzione, poi aggiornare done, e sbloccare dopo il completamento dell'esecuzione.
Do
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()
}
}Il codice è complessivamente molto semplice, il flusso è il seguente:
- Carica direttamente il valore atomico, se è già stato eseguito, restituisce direttamente
- Se non è stato eseguito, tenta di acquisire il lock. Qui potrebbero esserci più goroutine in competizione per il mutex
- Quando il vincitore acquisisce con successo il lock, deve ancora una volta verificare il valore di
done, perché dopo aver acquisito il lock, qualcun altro potrebbe aver completato l'intera sequenza di acquisizione-lock-esecuzione-sblocco, e tu sei appena stato risvegliato - Esegue la funzione target
- Aggiorna il valore di
done - Rilascia il lock
Riepilogo
sync.Once è uno strumento di sincronizzazione molto conciso ed efficiente, che garantisce che una determinata operazione venga eseguita una sola volta in un ambiente concorrente, evitando così lavori ripetuti o sprechi di risorse.
