Как послать сигнал закрытия в канал, чтобы все 15 горутин получили сигнал и завершились в Go
асинхронностьгорутинызавершение работыканалымногопоточность
Что проверяют
Знаете ли вы, что сигнал рассылается закрытием канала, а не отправкой сообщений по числу получателей.
Как отвечать
Отправка в канал достаётся ровно одному получателю, поэтому пятнадцать сообщений это неверный путь. Закрытие канала видят все сразу: чтение из закрытого канала мгновенно возвращает нулевое значение.
done := make(chan struct{})
var wg sync.WaitGroup
for i := 0; i < 15; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for {
select {
case <-done: // сработает у всех после close
return
case job := <-jobs:
process(job)
}
}
}()
}
close(done) // одна операция останавливает все пятнадцать
wg.Wait() // дожидаемся фактического завершенияЧто стоит добавить:
- пустая структура в канале не занимает памяти, значение здесь не нужно, важен сам факт закрытия;
context.WithCancelэто тот же приём в стандартной обёртке, и в современном коде берут именно его:ctx.Done()возвращает канал, который закрывается при отмене, а отмена распространяется по дереву вызовов вместе с таймаутами;- закрывать канал должен один владелец, повторное закрытие вызывает панику. Если сигнал могут подать несколько мест, используют
sync.Onceили контекст, у которого отмена идемпотентна; closeне ждёт завершения: он только сообщает. Дождаться реального выхода нужно группой ожидания;- длинная операция внутри горутины не прервётся сама: проверку сигнала нужно вставлять между шагами, иначе завершение затянется.
Где спотыкаются
Отправляют по сообщению на каждую горутину и получают гонку за то, кому достанется сигнал.
Закрывают канал из нескольких мест и ловят панику.
Считают, что после close горутины уже завершились, и выходят из программы, не дождавшись их.