Skip to content

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.

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()
}

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

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

Die interne Struktur ist sehr einfach:

  1. done, ein atomarer Wert, der anzeigt, ob die Funktion bereits ausgeführt wurde
  2. m, 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

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()
	}
}

Der Code ist insgesamt sehr einfach. Der Ablauf ist wie folgt:

  1. Direktes Laden des atomaren Wertes. Wenn bereits ausgeführt wurde, wird direkt zurückgekehrt.
  2. Wenn nicht ausgeführt wurde, wird versucht, die Sperre zu halten. Hier können mehrere Goroutinen um den Mutex konkurrieren.
  3. Nachdem der Gewinner erfolgreich die Sperre gehalten hat, muss der Wert von done erneut 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.
  4. Ausführung der Zielfunktion
  5. Aktualisierung des done-Wertes
  6. 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.

Golang by www.golangdev.cn edit