Есть три горутины, в которых запуск execute() заблокирован. Нужно, чтобы
release() по команде разблокировал все три worker() разом.
func startAll() {
go worker()
go worker()
go worker()
release()
// нужно гарантировать, что все три запустятся одновременно после этого вызова
}
func release() {
// write
}
func worker() {
// wait for release call
execute()
}
Оформи это как переиспользуемые «ворота»: воркеры ждут на Wait, а Release
открывает их всем сразу.
type Gate struct { /* … */ }
func NewGate() *Gate
// Wait блокируется, пока ворота не открыты.
func (g *Gate) Wait()
// Release открывает ворота для всех ожидающих и будущих Wait.
func (g *Gate) Release()
Требования
- До
Release ни один Wait не возвращается.
- После
Release возвращаются все ожидающие Wait, сколько бы их ни было:
три, сто или ноль.
Wait, вызванный после Release, возвращается сразу.
Release можно вызвать повторно, в том числе одновременно из нескольких
горутин. Это не ошибка и не паника.
- Код проходит
go test -race.
Пример
g := NewGate()
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go func() {
defer wg.Done()
g.Wait()
execute()
}()
}
time.Sleep(time.Second) // ни один execute ещё не запущен
g.Release() // все три стартуют
wg.Wait()
// WaitContext ждёт открытия ворот или отмены ctx.
func (g *Gate) WaitContext(ctx context.Context) error
- Возвращает
nil, когда ворота открыты, и ctx.Err(), если ctx отменили
раньше. После отмены горутина не остаётся висеть.
- Если ворота уже открыты, возвращает
nil, даже если ctx тоже отменён.
Устно: чем close(ch) лучше sync.Cond.Broadcast для этой задачи, а когда
нужен именно Cond? Как сделать ворота многоразовыми?
Разбор и эталонное решение — после своей попытки: на собеседовании подсказок не будет.
Сначала попробуй сам — подсказки не дают готовый ответ.