Метод sync.Once.Do
Метод sync.Once.Do принимает в качестве аргумента функцию без параметров и возвращаемых значений.
Он гарантирует, что эта функция будет вызвана только один раз, независимо от количества горутин,
которые одновременно вызывают Do на одном и том же экземпляре sync.Once.
Если функция вызывается повторно, Do ничего не делает и возвращает управление.
Это идеальный механизм для ленивой инициализации объектов, загрузки конфигураций или создания синглтонов.
Синтаксис
var once sync.Once
once.Do(func() {
// код, который должен выполниться один раз
})
Базовый пример
В этом примере sync.Once.Do используется для инициализации глобальной переменной.
Несмотря на то, что мы запускаем несколько горутин, инициализация произойдет только один раз.
package main
import (
"fmt"
"sync"
)
var (
config string
once sync.Once
)
func loadConfig() {
config = "config loaded"
fmt.Println("Config loaded once")
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
once.Do(loadConfig)
fmt.Println("Config:", config)
}()
}
wg.Wait()
}
Результат выполнения кода (порядок строк может отличаться, но сообщение о загрузке появится только один раз):
"Config loaded once"
"Config: config loaded"
"Config: config loaded"
"Config: config loaded"
"Config: config loaded"
"Config: config loaded"
Пример с паникой
Важно: если функция, переданная в Do, вызывает панику,
то Once считает, что вызов не был завершен успешно,
и при следующем вызове Do выполнит функцию снова.
Это поведение позволяет восстановиться после ошибок инициализации.
package main
import (
"fmt"
"sync"
)
func main() {
var once sync.Once
counter := 0
for i := 0; i < 3; i++ {
func() {
defer func() {
if r := recover(); r != nil {
fmt.Println("recovered from:", r)
}
}()
once.Do(func() {
counter++
fmt.Println("attempt:", counter)
if counter == 1 {
panic("initialization failed")
}
})
}()
}
fmt.Println("final counter:", counter)
}
Результат выполнения кода:
"attempt: 1"
"recovered from: initialization failed"
"attempt: 2"
"final counter: 2"
Смотрите также
-
тип
sync.Once,
который предоставляет механизм однократного выполнения -
тип
sync.WaitGroup,
который используется для ожидания завершения горутин -
тип
sync.Mutex,
который обеспечивает взаимное исключение при доступе к данным -
тип
sync.RWMutex,
который предоставляет блокировку с разделяемым доступом на чтение