Реализуйте обёртку над «тяжёлой» инициализацией, которую можно безопасно вызывать из сотен горутин сразу. Функция инициализации должна выполниться ровно один раз. Все остальные вызывающие ждут её завершения и получают тот же результат, то есть ту же ошибку или nil.
type Initializer struct { /* ... */ }
// NewInitializer принимает функцию тяжёлой инициализации.
func NewInitializer(init func() error) *Initializer
// Do запускает инициализацию при первом вызове и возвращает её результат.
func (i *Initializer) Do() error
Пример:
in := NewInitializer(func() error {
time.Sleep(20 * time.Millisecond) // подключение к БД, чтение конфига...
return errors.New("boom")
})
// 200 горутин одновременно вызывают in.Do()
// init выполнится один раз; каждая горутина получит ошибку "boom"
// повторный in.Do() позже тоже вернёт "boom" и init не запустит
Требования:
init вызывается ровно один раз за всё время жизни Initializer, даже при повторных вызовах Do() после завершения.
- Пока первый вызов выполняет
init, остальные блокируются и возвращаются только после его завершения.
- Ошибка из
init возвращается каждому вызывающему, включая тех, кто сам init не запускал.
- Нет гонок под
-race.
Тесты проверяют: init вызван один раз при 200 одновременных Do, ошибка доходит до всех (и до повторного вызова), и ни один Do не возвращается, пока init не завершился.
Сначала попробуй сам — подсказки не дают готовый ответ.