Реализуйте обёртку для «тяжёлой» инициализации, которая безопасна для вызова
из сотен горутин одновременно. Функция инициализации должна выполниться
ровно один раз, а все вызывающие — дождаться её завершения и получить
один и тот же результат (ошибку).
type Initializer struct {
// ...
}
// NewInitializer принимает функцию тяжёлой инициализации.
func NewInitializer(init func() error) *Initializer
// Do запускает инициализацию. Безопасно вызывать из сотен горутин:
// init выполнится ровно один раз, все вызывающие дождутся её и получат
// одну и ту же ошибку (или nil).
func (i *Initializer) Do() error
Тест передаёт init, которая атомарно инкрементирует счётчик и немного спит,
затем вызывает Do() из 200 горутин. Ожидается, что счётчик равен 1, и
каждый вызывающий увидел одинаковый результат. Проверка под -race.
На что смотрит интервьюер:
- Использование
sync.Once: init гарантированно выполняется один раз даже
при гонке вызовов.
- Результат (
error) сохраняется в поле и возвращается всем вызывающим,
включая тех, кто не выполнял init.
- Все вызывающие блокируются до завершения первой инициализации
(
Once.Do обеспечивает это автоматически), нет гонок под -race.