Я думаю, что вы правильно определили, что с goroutineRunning WaitGroup что-то не так.
Я думаю, что оригинальный код не включал WaitGroup, но тот, кто написал код, обнаружил, что произошла гонка. условие, когда Broadcast () вызывался (для переменной syn c .Cond) до того, как 3 go -программы вызвали Wait (). Использование goroutineRunning было попыткой исправить это, но оно не исключает условия гонки, а лишь снижает вероятность этого. Например, если вы перешли в режим сна после goroutineRunning.Done (), то у вас возникнет та же проблема - Broadcast () вызывается до того, как 3 go -программы ожидают.
Чтобы вернуться к исходному вопросу ... Перемещение goroutine.Done () вперед (в любом месте после вызова Wait) вызовет взаимоблокировку, так как вызов c .Wait () должен ожидать Broadcast (), который никогда не может прийти, поскольку первая подписка () будет никогда не возвращаться, пока не будет вызван goroutineRunning.Done () (чтобы разблокировать goroutineRunning.Wait ()).
Перемещение goroutineRunning.Done () до тех пор, пока c .Wait () не станет лучше, но не устранит гонку .
Чтобы исправить исходный код, вам нужно поставить gorutineRunning.Done () после вызова c .L.Lock (), а также заблокировать Broadcast ().
type Button struct {
Clicked *sync.Cond
}
button := Button{ Clicked: sync.NewCond(&sync.Mutex{}) }
subscribe := func(c *sync.Cond, fn func()) {
var goroutineRunning sync.WaitGroup
goroutineRunning.Add(1)
go func() {
c.L.Lock()
defer c.L.Unlock()
goroutineRunning.Done() // *** moved
c.Wait()
fn()
}()
goroutineRunning.Wait()
}
var clickRegistered sync.WaitGroup
clickRegistered.Add(3)
subscribe(button.Clicked, func() {
fmt.Println("Maximizing window.")
clickRegistered.Done()
})
subscribe(button.Clicked, func() {
fmt.Println("Displaying annoying dialog box!")
clickRegistered.Done()
})
subscribe(button.Clicked, func() {
fmt.Println("Mouse clicked.")
clickRegistered.Done()
})
button.Clicked.L.Lock() // *** new
button.Clicked.Broadcast()
button.Clicked.L.Unlock() // *** new
clickRegistered.Wait()
[Кстати, я принимаю, что это не ответ на ваш первоначальный вопрос (и что другой ответ должен получить очки), но я подумал, что стоит упомянуть.]