Сколько горутин получат один сигнал, отправленный в канал в Go
Что проверяют
Понимаете ли вы, что канал это очередь с одной точкой выдачи, а не широковещательная рассылка. Следом почти всегда спрашивают, как тогда разбудить сразу всех ожидающих и что будет с остальными горутинами в таком коде.
Как отвечать
Короткий ответ: одно значение, отправленное через ch <- v, получит ровно одна горутина. Дальше покажите, почему так и где исключения:
- очередь получателей: у канала внутри есть список ожидающих
recvq. Отправитель берёт первого из очереди и передаёт значение прямо ему, остальные продолжают спать. Какая именно горутина получит значение, спецификация языка не гарантирует, строить на этом логику нельзя; - буферизованный канал: значение ложится в буфер, и его заберёт тот, кто первым выполнит чтение. Буфер меняет момент блокировки отправителя, но не число получателей;
- закрытие канала:
close(ch)будит всех ожидающих разом. Каждый получит нулевое значение типа иok == falseв формеv, ok := <-ch. На этом построенctx.Done(): отмена контекста закрывает канал, и её видят все горутины сразу; select: если горутина ждёт в нескольких ветках, значение всё равно уходит одному получателю, а из нескольких готовых веток Go выбирает случайную.
Чтобы разослать одно событие всем, берут закрытие канала (работает один раз), отдельный канал на каждого подписчика или sync.Cond с методом Broadcast().
Где спотыкаются
Отвечают «получат все, кто слушает», путая канал с pub/sub. Если три воркера читают один канал, три значения разойдутся по одному на каждого, а не каждому по три.
Не замечают дедлок в собственном примере: запустили три получателя, отправили одно значение и зовут wg.Wait(). Две горутины навсегда повиснут на чтении, и рантайм остановит программу с fatal error: all goroutines are asleep.
Предлагают для рассылки закрывать канал каждый раз заново. Повторный close и отправка в закрытый канал вызывают панику, поэтому закрытие годится только как одноразовый сигнал, и закрывает канал отправитель, а не получатель.