Текущий дизайн среды выполнения Go предполагает, что программист отвечает за определение того, когда завершать программу и когда завершать программу.Программист должен вычислить условие завершения для подпрограмм, а также для всей программы.Завершение программы можно обычным способом, вызвав os.Exit
или вернувшись из функции main()
.
Создание канала и задержка выхода из main()
путем немедленного приема науказанный канал является допустимым способом предотвращения выхода main
.Но это не решает проблему определения того, когда завершать программу.
Если число подпрограмм не может быть вычислено до того, как функция main()
войдет в цикл ожидания всех горутин до завершения,вам нужно посылать дельты, чтобы функция main
могла отслеживать, сколько горутин в полете:
// Receives the change in the number of goroutines
var goroutineDelta = make(chan int)
func main() {
go forever()
numGoroutines := 0
for diff := range goroutineDelta {
numGoroutines += diff
if numGoroutines == 0 { os.Exit(0) }
}
}
// Conceptual code
func forever() {
for {
if needToCreateANewGoroutine {
// Make sure to do this before "go f()", not within f()
goroutineDelta <- +1
go f()
}
}
}
func f() {
// When the termination condition for this goroutine is detected, do:
goroutineDelta <- -1
}
Альтернативный подход - заменить канал на sync.WaitGroup
,Недостаток этого подхода заключается в том, что wg.Add(int)
необходимо вызывать перед вызовом wg.Wait()
, поэтому необходимо создать как минимум 1 подпрограмму в main()
, тогда как последующие подпрограммы могут быть созданы в любой части программы:
var wg sync.WaitGroup
func main() {
// Create at least 1 goroutine
wg.Add(1)
go f()
go forever()
wg.Wait()
}
// Conceptual code
func forever() {
for {
if needToCreateANewGoroutine {
wg.Add(1)
go f()
}
}
}
func f() {
// When the termination condition for this goroutine is detected, do:
wg.Done()
}