once
sync.Once ist ein Synchronisationswerkzeug in der Go-Standardbibliothek. Es wird verwendet, um sicherzustellen, dass eine bestimmte Funktion in einer nebenläufigen Umgebung nur einmal ausgeführt wird. Es wird häufig für verzögerte Initialisierung, Initialisierung globaler Ressourcen und ähnliche Szenarien verwendet, um sicherzustellen, dass eine bestimmte Operation nur einmal ausgeführt wird, selbst wenn mehrere Goroutinen gleichzeitig ausgeführt werden.
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()
}Im obigen Code gibt es insgesamt 10 Goroutinen. Aber egal was passiert, nur eine Goroutine wird das fmt.Println(i) in once.Do() ausführen. Welche Goroutine genau druckt, hängt davon ab, welche Goroutine zuerst bei once.Do() ankommt.
Struktur
type Once struct {
done atomic.Uint32
m Mutex
}Die interne Struktur ist sehr einfach:
done, ein atomarer Wert, der anzeigt, ob die Funktion bereits ausgeführt wurdem, ein Mutex, der verwendet wird, um andere Goroutinen zu blockieren, die ausführen möchten
Das Prinzip besteht darin, vor der Ausführung zuerst zu sperren, dann done zu aktualisieren, und nach Abschluss der Ausführung zu entsperren.
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()
}
}Der Code ist insgesamt sehr einfach. Der Ablauf ist wie folgt:
- Direktes Laden des atomaren Wertes. Wenn bereits ausgeführt wurde, wird direkt zurückgekehrt.
- Wenn nicht ausgeführt wurde, wird versucht, die Sperre zu halten. Hier können mehrere Goroutinen um den Mutex konkurrieren.
- Nachdem der Gewinner erfolgreich die Sperre gehalten hat, muss der Wert von
doneerneut geprüft werden. Denn nachdem man die Sperre gehalten hat, könnte jemand anderes bereits den kompletten Prozess aus Sperren-Ausführen-Entsperren abgeschlossen haben, während man gerade erst aufgeweckt wurde. - Ausführung der Zielfunktion
- Aktualisierung des
done-Wertes - Freigabe der Sperre
Zusammenfassung
sync.Once ist ein sehr einfaches und effizientes Synchronisationswerkzeug. Es stellt sicher, dass eine bestimmte Operation in einer nebenläufigen Umgebung nur einmal ausgeführt wird, wodurch doppelte Arbeit oder Ressourcenverschwendung vermieden wird.
