Следите за новинками
в нашем Telegram канале. Жми, чтобы подписаться:)
⊗goSpSyOn 170 of 280 menu
◀ ▶

Один раз в Go

Иногда тяжелую настройку или запись в общую переменную нужно сделать единожды, хотя к ней обращаются из многих горутин. Повторный запуск даст лишнюю работу или испортит состояние, если код не рассчитан на второй заход.

Тип sync.Once хранит флаг завершения. Метод Do принимает функцию без аргументов: первый вызов выполняет ее тело, все последующие из любых веток сразу выходят, не заходя внутрь.

Три горутины зовут одну инициализацию:

package main import ( "fmt" "sync" ) var setup sync.Once var ready bool func initOnce() { setup.Do(func() { ready = true fmt.Println("init") }) } func main() { var wg sync.WaitGroup wg.Add(3) for i := 0; i < 3; i++ { go func() { defer wg.Done() initOnce() }() } wg.Wait() fmt.Println("ready", ready) }

Строка "init" печатается один раз, хотя initOnce вызвали трижды. ready остается истиной после единственного прохода внутри Do.

Тот же прием для счетчика вызовов:

package main import ( "fmt" "sync" ) func main() { var once sync.Once var hits int var wg sync.WaitGroup wg.Add(5) for i := 0; i < 5; i++ { go func() { defer wg.Done() once.Do(func() { hits++ fmt.Println("once") }) }() } wg.Wait() fmt.Println("hits", hits) }

Дан следующий код:

var flag sync.Once var msg string func fill() { flag.Do(func() { msg = "ok" }) }

Запустите fill из двух веток с ожиданием группы и выведите msg.

Дан следующий код:

var o sync.Once func run() { o.Do(func() { fmt.Println("A") }) o.Do(func() { fmt.Println("B") }) }

Скажите, сколько букв напечатает один вызов run.

Скажите, зачем передавать в Do функцию, а не вызывать обычный код рядом с проверкой флага вручную.

← →
↑
Мы используем cookie для работы сайта, аналитики и персонализации. Обработка данных происходит согласно Политике конфиденциальности.
принять все настроить отклонить