Ответ
В Go существует два вида каналов, которые различаются поведением при отправке и получении данных: небуферизированные и буферизированные.
1. Небуферизированный (синхронный) канал
- Создание:
ch := make(chan int)
- Поведение: У такого канала нет буфера. Операция отправки (
ch <- value
) блокируется до тех пор, пока другая горутина не будет готова принять данные из этого канала (<-ch
). Аналогично, операция приема блокируется, пока другая горутина не отправит данные. Это делает канал синхронным и гарантирует, что отправитель и получатель "встретились". - Пример (Deadlock): Следующий код вызовет панику, так как отправка в канал блокирует
main
, и никто не сможет из него прочитать.// fatal error: all goroutines are asleep - deadlock! ch := make(chan int) ch <- 1 // Блокировка навсегда fmt.Println(<-ch)
- Правильное использование: Отправка и прием должны происходить в разных горутинах.
ch := make(chan int) go func() { ch <- 42 // Отправка в другой горутине }() val := <-ch // Прием в main fmt.Println(val) // 42
2. Буферизированный (асинхронный) канал
- Создание:
ch := make(chan int, N)
, гдеN > 0
— размер буфера. - Поведение: У канала есть буфер размером
N
.- Операция отправки не блокируется, пока в буфере есть свободное место. Если буфер полон, отправка блокируется до тех пор, пока кто-нибудь не заберет значение.
- Операция приема не блокируется, пока в буфере есть данные. Если буфер пуст, прием блокируется до появления новых данных. Это делает канал асинхронным.
-
Пример:
// Буфер на 2 элемента ch := make(chan int, 2) ch <- 1 // Не блокируется ch <- 2 // Не блокируется // ch <- 3 // Эта операция заблокируется, т.к. буфер полон fmt.Println(<-ch) // 1 fmt.Println(<-ch) // 2
Когда что использовать?
- Небуферизированные каналы: Используйте для гарантированной синхронизации между горутинами. Когда вы отправляете значение, вы уверены, что кто-то его получил в тот же момент.
- Буферизированные каналы: Используйте для расцепления (decoupling) горутин. Они полезны, когда отправитель и получатель работают с разной скоростью, или для реализации паттернов, таких как пул воркеров, где буферизированный канал выступает в роли очереди задач.