Ответ
В Go паттерн 'вещание' (broadcast) можно реализовать несколькими способами, каждый со своими особенностями:
-
Закрытие канала (one-time broadcast) Это самый простой способ. При закрытии канала все горутины, ожидающие чтения из него, немедленно разблокируются и получат нулевое значение типа канала.
- Плюсы: Простота реализации.
- Минусы: Это одноразовый сигнал. После закрытия канал нельзя использовать для отправки данных.
broadcastChan := make(chan struct{})
// В подписчиках: go func() { <-broadcastChan fmt.Println("Горутина 1 получила сигнал!") }()
// Вещатель: close(broadcastChan) // Все горутины получат сигнал
-
Рассылка по нескольким каналам (Fan-out) Создается отдельный канал для каждого подписчика, и вещатель отправляет сообщение в каждый из них.
- Плюсы: Позволяет передавать данные, а не только сигналы.
- Минусы: Если один из каналов-получателей не готов принять сообщение (заблокирован), то весь цикл рассылки остановится. Эту проблему можно решить, используя неблокирующую отправку с
select.// subscribers - это slice или map с каналами подписчиков for _, ch := range subscribers { // Неблокирующая отправка, чтобы не зависнуть на одном подписчике select { case ch <- message: default: // Сообщение будет пропущено, если подписчик не готов } }
-
Использование
sync.Condsync.Cond— это условная переменная, которая позволяет горутинам ждать наступления определенного события. Это более низкоуровневый и производительный механизм для синхронизации.cond.Broadcast()— пробуждает все ожидающие горутины.cond.Signal()— пробуждает одну случайную горутину.- Плюсы: Высокая производительность, предназначен специально для таких задач.
var mu sync.Mutex cond := sync.NewCond(&mu)
// Получатель (в горутине): cond.L.Lock() cond.Wait() // Ждет сигнала // ... делает работу cond.L.Unlock()
// Вещатель: cond.Broadcast() // Пробуждает всех
-
Реализация паттерна Pub/Sub Это наиболее полный и гибкий подход. Создается центральный менеджер (в виде горутины), который управляет списком подписчиков (добавление/удаление) и рассылает им сообщения. Обычно это комбинация второго подхода с защитой доступа к списку подписчиков через мьютекс.
Ответ 18+ 🔞
А, паттерн вещания в Go, да? Ну это ж классика, как "Муму" Тургенева, только без утопления собаки, блядь.
Смотри, вариантов дохуя, и каждый со своей, блядь, философией. Как в том анекдоте про ткачиху — можно просто зашить, а можно полотна наткать, чтобы все заеблись.
Первый способ — закрытие канала. Это как одноразовый сигнал, ёпта. Закрыл канал — и все, кто из него читал, тут же просыпаются. Просто, как три копейки, но и одноразово, блядь, как спичка.
broadcastChan := make(chan struct{})
// В подписчиках:
go func() {
<-broadcastChan // Сидим, ждём, как Герасим у лестницы
fmt.Println("Горутина 1 получила сигнал!")
}()
// Вещатель:
close(broadcastChan) // Хлоп! И все побежали
Плюсы? Да нихуя сложного. Минусы? После этого канал — труп, в него больше ни хуя не отправишь.
Второй способ — рассылка по куче каналов (Fan-out). Тут уже посерьёзнее. У каждого подписчика свой личный канал, и ты, как почтальон Печкин, бегаешь и всем суёшь сообщение в щель.
for _, ch := range subscribers {
select {
case ch <- message: // Отправил — и молодца
default: // А этот, сука, не готов, засранец. Ну и хуй с ним, пропускаем.
}
}
Плюс — можно данные передавать, а не только пинок под зад. Минус — если один подписчик тормозит и не читает, то на нём весь цикл встанет колом, если без select. А с select и default — проебёшь сообщение. Волнение ебать!
Третий способ — sync.Cond.
Вот это, блядь, тяжёлая артиллерия. Низкоуровневая синхронизация, овердохуища производительности. Условная переменная, мать её.
cond.Broadcast()— это как крикнуть на всю деревню: "ПОЖАААР!" — все высыпят.cond.Signal()— это как ткнуть одного в бок: "Проснись, мудак".var mu sync.Mutex cond := sync.NewCond(&mu) // Получатель: cond.L.Lock() cond.Wait() // Сижу, курю, жду у моря погоды cond.L.Unlock() // Вещатель: cond.Broadcast() // ВСЕМ ВСТАТЬ! СУД ИДЁТ!Плюсы — быстрее некуда, для этого и создан. Минусы — с низкоуровневыми примитивами всегда можно так накосячить, что мало не покажется. Подозрение ебать чувствую, когда его используют.
Четвёртый способ — полноценный Pub/Sub. Это уже, блядь, целый театр с директором. Отдельная горутина-менеджер, которая держит список всех подписчиков, охраняемый мьютексом, как зеницу ока. Добавляй, удаляй, рассылай — красота. Это комбинация второго способа, но причёсанная и поставленная на поток. Гибко, мощно, но и кода писать — овердохуища.
Вот и вся история, как с Герасимом. Можно просто и грубо — закрыть канал и утопить всех в озере. А можно выстроить целую систему отношений с подписчиками, где каждому — по письму, каждому — по сигналу. Выбирай, что душе угодно, только смотри, чтобы в конце не оказаться тем самым мудаком, который спрашивает: "Что же я, блядь, наделал?"