Ответ
Каналы в Go — это основной механизм для безопасной и синхронизированной коммуникации между горутинами. Они позволяют избежать состояния гонки при доступе к общим данным.
Ключевые свойства:
-
Типизированность (Typed): Канал может передавать данные только одного, заранее определенного типа. Это обеспечивает безопасность типов на этапе компиляции.
-
Синхронизация и блокировка (Synchronization & Blocking):
- Небуферизованный канал (
make(chan T)
): Отправка (ch <- data
) блокирует горутину-отправителя до тех пор, пока другая горутина не будет готова принять данные (<-ch
), и наоборот. Это обеспечивает точку синхронизации. - Буферизованный канал (
make(chan T, N)
): Отправка блокируется только когда буфер полон, а чтение — только когда буфер пуст. Это позволяет горутинам работать более независимо.
- Небуферизованный канал (
-
Направленность (Directional): Тип канала можно ограничить, разрешив только отправку (
chan<- T
) или только чтение (<-chan T
). Это полезно для проектирования безопасных API, где функция либо производит данные, либо потребляет их. -
Закрытие (Closing): Канал можно закрыть с помощью
close(ch)
, что сигнализирует о том, что больше данных отправлено не будет.- Запись в закрытый канал вызывает панику.
- Чтение из закрытого канала не блокируется: оно возвращает оставшиеся в буфере значения, а затем — нулевое значение для типа канала и
false
в качестве второго возвращаемого значения (val, ok := <-ch
).
-
Нулевое значение (Zero Value): Нулевое значение для канала —
nil
. Любые операции отправки или получения наnil
-канале блокируются навсегда.
Пример использования с for range
:
package main
import (
"fmt"
"time"
)
func main() {
jobs := make(chan int, 5)
done := make(chan bool)
go func() {
// Итерируемся по каналу, пока он не будет закрыт
for j := range jobs {
fmt.Println("received job", j)
time.Sleep(time.Second)
}
fmt.Println("all jobs received")
done <- true
}()
for j := 1; j <= 3; j++ {
jobs <- j
fmt.Println("sent job", j)
}
close(jobs) // Закрываем канал, чтобы range завершился
fmt.Println("sent all jobs")
<-done // Ожидаем завершения горутины-воркера
}