Вопрос проверяет понимание механизма однократного выполнения в Go и его применения для ленивой инициализации и гарантии однократного выполнения кода.
sync.Once — это примитив синхронизации из стандартной библиотеки Go, который гарантирует, что переданная функция будет выполнена ровно один раз, независимо от количества вызовов метода Do() из разных горутин. Он идеально подходит для ленивой инициализации (lazy initialization) и создания синглтонов.
sync.Once использует внутренний мьютекс и атомарный флаг. При первом вызове Do() он блокирует мьютекс, проверяет флаг, и если функция еще не выполнялась, запускает её. После завершения флаг устанавливается, и последующие вызовы Do() сразу возвращаются, не выполняя функцию.
package main
import (
"fmt"
"sync"
)
var once sync.Once
var config map[string]string
func loadConfig() {
fmt.Println("Loading config...")
config = map[string]string{"key": "value"}
}
func getConfig() map[string]string {
once.Do(loadConfig)
return config
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println(getConfig())
}()
}
wg.Wait()
}В этом примере loadConfig() выполнится только один раз, даже если getConfig() вызывается из 10 горутин одновременно.
sync.Once — простой и эффективный инструмент для гарантии однократного выполнения кода в многопоточной среде. Его стоит применять везде, где требуется ленивая инициализация или создание синглтонов с потокобезопасностью.