Ce face sync.Once
once.Do(f) rulează f doar la primul apel; toate apelurile următoare nu mai fac nimic. Dacă mai multe goroutine apelează Do în același timp, una rulează f, iar celelalte așteaptă să termine, deci după Do inițializarea este sigur gata.
Se folosește pentru inițializări costisitoare făcute doar când e nevoie: conexiunea la baza de date, încărcarea unei configurări, compilarea unor expresii regulate. Pentru variantele care returnează o valoare, vezi sync.OnceValue și sync.OnceFunc.
Dacă f se oprește cu panică, Once consideră totuși că a rulat; apelurile următoare nu mai încearcă.
Parametri
| Parametru | Descriere |
|---|---|
f |
Funcția de inițializare, de forma func(). |
Ce returnează
Nimic.
Exemple
Exemplul 1: Configurare încărcată o singură dată
Zece goroutine cer configurarea; mesajul „Încarc configurarea” apare o singură dată.
package main
import (
"fmt"
"sync"
)
var (
once sync.Once
setari map[string]string
)
func configurare() map[string]string {
once.Do(func() {
fmt.Println("Încarc configurarea...")
setari = map[string]string{"oras": "Iași", "moneda": "lei"}
})
return setari
}
func main() {
var wg sync.WaitGroup
for range 10 {
wg.Go(func() {
_ = configurare()["oras"]
})
}
wg.Wait()
fmt.Println("Oraș:", configurare()["oras"], "- monedă:", configurare()["moneda"])
}
Rezultat
Încarc configurarea...
Oraș: Iași - monedă: lei
Exemplul 2: Once într-o structură
Fiecare obiect are propriul Once, deci se inițializează separat.
package main
import (
"fmt"
"strings"
"sync"
)
type Document struct {
text string
once sync.Once
cuvinte int
}
func (d *Document) NumarCuvinte() int {
d.once.Do(func() {
fmt.Println(" (număr cuvintele o dată)")
d.cuvinte = len(strings.Fields(d.text))
})
return d.cuvinte
}
func main() {
d := &Document{text: "Go este un limbaj simplu și rapid"}
fmt.Println("Cuvinte:", d.NumarCuvinte())
fmt.Println("Cuvinte:", d.NumarCuvinte())
alt := &Document{text: "Salut Iași"}
fmt.Println("Alt document:", alt.NumarCuvinte())
}
Rezultat
(număr cuvintele o dată)
Cuvinte: 7
Cuvinte: 7
(număr cuvintele o dată)
Alt document: 2
Greșeli frecvente
- Apelezi
once.Dodin interiorul funcției date aceluiașionce.Do: programul se blochează (Doașteaptă propria funcție). - Te aștepți ca
Oncesă reîncerce după o eroare. Dacă inițializarea poate eșua și vrei să reîncerci, folosește un mutex și o variabilă proprie. - Folosești
if !initializat { ... }cu unboolobișnuit. Între goroutine este o cursă de date;sync.Oncerezolvă corect cazul.
Comenzi înrudite
sync.OnceFunc: Returnează o funcție care rulează f doar la primul apel; apelurile următoare nu fac nimic.sync.OnceValue: Returnează o funcție care calculează valoarea o singură dată și o dă înapoi la fiecare apel; OnceValues pentru două valori.sync.Mutex: Lacăt de excludere reciprocă: doar un goroutine pe rând poate rula codul dintre Lock() și Unlock().