Ответ
Каналы — это ключевой инструмент для коммуникации и синхронизации между горутинами, реализующий философию Go: "Do not communicate by sharing memory; instead, share memory by communicating."
Их целесообразно использовать в следующих сценариях:
-
Паттерн Worker Pool: Распределение задач между несколькими горутинами-воркерами.
- Один канал (
jobs
) используется для отправки задач. - Другой канал (
results
) — для сбора результатов. - Это позволяет эффективно распараллелить CPU-bound или I/O-bound операции.
jobs := make(chan int, 100) results := make(chan int, 100)
// Запуск 3 воркеров for w := 1; w <= 3; w++ { go func() { for j := range jobs { // какая-то работа results <- j * 2 } }() }
- Один канал (
-
Синхронизация и сигнализация: Управление жизненным циклом горутин.
- Сигнал о завершении: Закрытие канала (
close(done)
) сигнализирует всем ожидающим горутинам, что пора завершать работу. - Ожидание события: Горутина может блокироваться на чтении из канала, пока другая горутина не отправит в него данные, сигнализируя о наступлении события.
- Сигнал о завершении: Закрытие канала (
-
Rate Limiting (Ограничение частоты запросов): Буферизованный канал можно использовать как семафор или ведро с токенами для ограничения количества одновременно выполняемых операций.
// Разрешаем не более 10 одновременных операций limiter := make(chan struct{}, 10) for _, task := range tasks { limiter <- struct{}{} // Занимаем слот go func(t Task) { defer func() { <-limiter }() // Освобождаем слот process(t) }(task) }
-
Мультиплексирование с
select
: Обработка событий из нескольких источников (каналов) одновременно.select
позволяет горутине ждать данных сразу из нескольких каналов и реагировать на первое поступившее сообщение.
Буферизованные vs. Небуферизованные каналы
- Небуферизованные (
make(chan T)
): Отправка блокируется, пока получатель не будет готов принять данные. Используются для гарантированной синхронизации. - Буферизованные (
make(chan T, N)
): Отправка не блокируется, пока в буфере есть место. Используются для асинхронной коммуникации и повышения производительности, когда скорости отправителя и получателя могут различаться.
В простых случаях, когда нужно лишь дождаться завершения группы горутин, sync.WaitGroup
может быть более простым и эффективным решением.