Ответ
Канал (channel) — это типизированный канал связи в Go, который позволяет горутинам безопасно обмениваться данными без явных блокировок (мьютексов). Это один из ключевых примитивов конкурентности в Go.
Основные свойства:
- Безопасность (Thread-safe): Каналы обеспечивают синхронизацию, гарантируя, что только одна горутина может получить доступ к данным в момент времени. Это предотвращает состояние гонки (race condition).
- Типизация: Каждый канал имеет тип данных, которые он может передавать (например,
chan int
,chan string
). - Блокировка: Операции отправки и получения данных блокируют горутину до тех пор, пока другая горутина не будет готова к парной операции.
Типы каналов:
Небуферизованный канал (
make(chan T)
):- Не имеет ёмкости. Отправка данных блокируется до тех пор, пока другая горутина не будет готова их принять. Это обеспечивает точку синхронизации.
Буферизованный канал (
make(chan T, N)
):- Имеет буфер размером
N
. Отправка блокируется только тогда, когда буфер полон. Получение блокируется, когда буфер пуст. Используется для асинхронного обмена данными.
- Имеет буфер размером
Основные операции:
- Отправка:
ch <- value
- Получение:
value := <-ch
- Проверка и получение:
value, ok := <-ch
ok
будетfalse
, если канал закрыт и пуст. Это идиоматичный способ проверить, что канал закрыт.
- Закрытие:
close(ch)
- Сигнализирует, что больше данных отправлено не будет. Закрывать канал всегда должен отправитель.
Важные правила:
- Отправка в закрытый канал вызывает панику.
- Получение из закрытого канала немедленно возвращает нулевое значение для типа канала.
- Закрытие уже закрытого канала вызывает панику.
Пример с проверкой закрытия:
ch := make(chan int, 2)
go func() {
defer close(ch) // Отправитель закрывает канал
ch <- 1
ch <- 2
}()
// Получатель читает данные, пока канал не закроется
for val := range ch {
fmt.Println(val) // Использование range - самый удобный способ
}
// Альтернативный способ с 'comma, ok'
val, ok := <-ch // После цикла val будет 0, ok будет false